├── .coveragerc ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md └── workflows │ ├── codeql-analysis.yml │ ├── documentation.yaml │ ├── ossar-analysis.yml │ ├── python_package.yaml │ └── python_publish.yaml ├── .gitignore ├── .readthedocs.yaml ├── CHANGELOG.rst ├── LICENSE.rst ├── MANIFEST.in ├── README.rst ├── docs ├── publications │ ├── cmame2020.bib │ └── swx2020.bib ├── requirements.txt └── source │ ├── .gitignore │ ├── _static │ ├── favicon.ico │ ├── github.svg │ ├── logo │ │ ├── browserconfig.xml │ │ ├── logo.png │ │ ├── logo.svg │ │ ├── logo_114x114.png │ │ ├── logo_120x120.png │ │ ├── logo_128x128.png │ │ ├── logo_144x144.png │ │ ├── logo_180x180.png │ │ ├── logo_192x192.png │ │ ├── logo_196x196.png │ │ ├── logo_228x228.png │ │ ├── logo_32x32.png │ │ ├── logo_57x57.png │ │ ├── logo_72x72.png │ │ ├── logo_76x76.png │ │ ├── logo_96x96.png │ │ ├── pad_logo.png │ │ ├── pad_logo.svg │ │ └── social.png │ ├── pypi.svg │ └── rtd.svg │ ├── _templates │ └── layout.html │ ├── api │ ├── cli.rst │ ├── geometry │ │ ├── box.rst │ │ ├── circle.rst │ │ ├── cube.rst │ │ ├── ellipse.rst │ │ ├── ellipsoid.rst │ │ ├── factory.rst │ │ ├── index.rst │ │ ├── n_box.rst │ │ ├── n_sphere.rst │ │ ├── rectangle.rst │ │ ├── sphere.rst │ │ └── square.rst │ ├── index.rst │ ├── meshing │ │ ├── index.rst │ │ ├── polymesh.rst │ │ ├── rastermesh.rst │ │ └── trimesh.rst │ ├── seeding │ │ ├── index.rst │ │ ├── seed.rst │ │ └── seedlist.rst │ └── verification.rst │ ├── changelog.rst │ ├── cli │ ├── domain.rst │ ├── index.rst │ ├── introduction.rst │ ├── material.rst │ └── settings.rst │ ├── conf.py │ ├── contents.rst │ ├── docutils.conf │ ├── examples │ ├── cli │ │ ├── basalt.rst │ │ ├── colormap.rst │ │ ├── elliptical_grains.rst │ │ ├── minimal.rst │ │ └── two_phase_3d.rst │ ├── index.rst │ ├── intro │ │ ├── intro_1.rst │ │ ├── intro_2.rst │ │ ├── intro_3.rst │ │ ├── intro_4.rst │ │ ├── intro_5.rst │ │ └── intro_6.rst │ └── package │ │ ├── foam.rst │ │ ├── from_image.rst │ │ ├── grain_neighborhoods.rst │ │ ├── logo.rst │ │ ├── mesh_process.rst │ │ ├── standard_voronoi.rst │ │ └── uniform_seeding.rst │ ├── file_formats.rst │ ├── getting_started.rst │ ├── index.rst │ ├── package_guide.rst │ ├── sphinx_gallery │ ├── README.rst │ ├── geometry │ │ ├── README.rst │ │ ├── plot_ellipse.py │ │ └── plot_rectangle.py │ └── plot_demos.py │ ├── troubleshooting.rst │ └── welcome.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── src └── microstructpy │ ├── __init__.py │ ├── _misc.py │ ├── cli.py │ ├── examples │ ├── .gitignore │ ├── aluminum_micro.png │ ├── aphanitic_cdf.csv │ ├── basalt_circle.xml │ ├── colormap.xml │ ├── docs_banner.py │ ├── elliptical_grains.xml │ ├── foam.py │ ├── from_image.py │ ├── grain_neighborhoods.py │ ├── intro_1_basic.xml │ ├── intro_2_quality.xml │ ├── intro_3_size_shape.xml │ ├── intro_4_oriented.xml │ ├── intro_5_plotting.xml │ ├── intro_6_culmination.xml │ ├── logo.py │ ├── minimal.xml │ ├── minimal_paired.xml │ ├── olivine_cdf.csv │ ├── standard_voronoi.py │ ├── two_phase_3D.xml │ └── uniform_seeding.py │ ├── geometry │ ├── __init__.py │ ├── box.py │ ├── circle.py │ ├── ellipse.py │ ├── ellipsoid.py │ ├── n_box.py │ ├── n_sphere.py │ ├── rectangle.py │ └── sphere.py │ ├── meshing │ ├── __init__.py │ ├── polymesh.py │ └── trimesh.py │ ├── seeding │ ├── __init__.py │ ├── seed.py │ └── seedlist.py │ └── verification.py ├── tests ├── cli │ ├── test_includes.py │ └── test_includes_files │ │ ├── different_dir │ │ └── input.xml │ │ ├── expected_input.xml │ │ ├── fine_grained.xml │ │ ├── input.xml │ │ └── materials.xml ├── geometry │ └── test_n_sphere.py ├── meshing │ └── test_polymesh.py └── test_misc.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | src/microstructpy/cli.py 4 | src/microstructpy/__init__.py 5 | src/microstructpy/*/__init__.py 6 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at conduct@microstructpy.org. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Contributing to MicroStructPy 5 | 6 | Whether you are a novice or experienced software developer, 7 | all contributions and suggestions are welcome! 8 | 9 | ## Getting Started 10 | 11 | If you are looking to contribute to the *MicroStructPy* codebase, 12 | the best place to start is the 13 | [GitHub "issues" tab](https://github.com/kip-hart/MicroStructPy/issues). 14 | This is also a great place for filing bug reports and making suggestions for 15 | ways in which we can improve the code and documentation. 16 | 17 | ## Filing Issues 18 | 19 | If you notice a bug in the code or documentation, or have suggestions for 20 | how we can improve either, feel free to create an issue on the 21 | [GitHub "issues" tab](https://github.com/kip-hart/MicroStructPy/issues) using 22 | [GitHub's "issue" form](https://github.com/kip-hart/MicroStructPy/issues/new). 23 | The form contains some questions that will help us best address your issue. 24 | 25 | ## Contributing to the Codebase 26 | 27 | The code is hosted on [GitHub](https://www.github.com/kip-hart/MicroStructPy), 28 | so you will need to use [Git](http://git-scm.com/) to clone the project and 29 | make changes to the codebase. 30 | Once you have obtained a copy of the code, you should create a development 31 | environment that is separate from your existing Python environment so that 32 | you can make and test changes without compromising your own work environment. 33 | Consider using the Python 34 | [venv](https://docs.python.org/3/library/venv.html#module-venv) module to 35 | create a development environment. 36 | 37 | Before submitting your changes for review, make sure to check that your 38 | changes do not break any tests. 39 | Install [tox](https://tox.readthedocs.io) and run it from the top-level project 40 | directory. 41 | Tox will run tests on the code, the documentation, the build, and the code 42 | style. 43 | Please ensure that all tests are passing before pushing to the repository. 44 | 45 | Once your changes are ready to be submitted, make sure to push your changes to 46 | GitHub before creating a pull request. 47 | We will review your changes, and you may be asked to make additional changes 48 | before it is finally ready to merge. 49 | However, once it's ready, we will merge it, and you will have successfully 50 | contributed to the codebase! 51 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | tidelift: "pypi/microstructpy" 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Summary 2 | ### Purpose 3 | _Describe the problem or feature in addition to a link to the issues._ 4 | 5 | ### Approach 6 | _How does this change address the problem?_ 7 | 8 | ### Learning 9 | _Describe the research stage_ 10 | 11 | _Links to blog posts, patterns, libraries or addons used to solve this problem_ 12 | 13 | ## PR Checklist 14 | - [ ] All ``tox`` commands succeed 15 | - [ ] Docs have been added / updated (for bug fixes / features) 16 | - [ ] Has pytest style unit tests 17 | 18 | ## Closing Issues 19 | 20 | Fixes # 21 | 22 | 23 | 51 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Brand Promise 2 | 3 | The MicroStructPy team and community take security bugs in MicroStructPy seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. 4 | 5 | # Scope 6 | 7 | If you believe you've found a security issue in software that is maintained in this repository, we encourage you to notify us. 8 | 9 | | Version | In scope | Source code | 10 | | ------- | ------------------ | ----------------------------------------------------- | 11 | | latest | :white_check_mark: | https://github.com/kip-hart/MicroStructPy | 12 | | v1.1.0 | :white_check_mark: | https://github.com/kip-hart/MicroStructPy/tree/v1.1.0 | 13 | | v1.0.1 | :white_check_mark: | https://github.com/kip-hart/MicroStructPy/tree/v1.0.1 | 14 | | v1.0.0 | :white_check_mark: | https://github.com/kip-hart/MicroStructPy/tree/v1.0.0 | 15 | 16 | # How to Submit a Report 17 | 18 | To submit a vulnerability report, please use Tidelift (https://tidelift.com/security). Your submission will be reviewed and validated by a member of our team. 19 | 20 | # Safe Harbor 21 | 22 | We support safe harbor for security researchers who: 23 | 24 | * Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our services. 25 | * Only interact with accounts you own or with explicit permission of the account holder. If you do encounter Personally Identifiable Information (PII) contact us immediately, do not proceed with access, and immediately purge any local information. 26 | * Provide us with a reasonable amount of time to resolve vulnerabilities prior to any disclosure to the public or a third-party. 27 | 28 | We will consider activities conducted consistent with this policy to constitute "authorized" conduct and will not pursue civil action or initiate a complaint to law enforcement. We will help to the extent we can if legal action is initiated by a third party against you. 29 | 30 | Please submit a report to us before engaging in conduct that may be inconsistent with or unaddressed by this policy. 31 | 32 | # Preferences 33 | 34 | * Please provide detailed reports with reproducible steps and a clearly defined impact. 35 | * Include the version number of the vulnerable package in your report 36 | * Social engineering (e.g. phishing, vishing, smishing) is prohibited. 37 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master, ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 20 * * 3' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v3 20 | with: 21 | # We must fetch at least the immediate parents so that if this is 22 | # a pull request then we can checkout the head. 23 | fetch-depth: 2 24 | 25 | # Initializes the CodeQL tools for scanning. 26 | - name: Initialize CodeQL 27 | uses: github/codeql-action/init@v1 28 | # Override language selection by uncommenting this and choosing your languages 29 | # with: 30 | # languages: go, javascript, csharp, python, cpp, java 31 | 32 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 33 | # If this step fails, then you should remove it and run the build manually (see below) 34 | - name: Autobuild 35 | uses: github/codeql-action/autobuild@v1 36 | 37 | # ℹ️ Command-line programs to run using the OS shell. 38 | # 📚 https://git.io/JvXDl 39 | 40 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 41 | # and modify them (or add more) to build your code if your project 42 | # uses a compiled language 43 | 44 | #- run: | 45 | # make bootstrap 46 | # make release 47 | 48 | - name: Perform CodeQL Analysis 49 | uses: github/codeql-action/analyze@v1 50 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yaml: -------------------------------------------------------------------------------- 1 | 2 | name: Documentation check 3 | 4 | on: [pull_request] 5 | 6 | jobs: 7 | docs-checks: 8 | name: ${{ matrix.doc-type }} 9 | strategy: 10 | matrix: 11 | doc-type: [html, latex, epub] 12 | 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@main 17 | - name: Set up Python 18 | uses: actions/setup-python@main 19 | with: 20 | python-version: '3.10' 21 | - name: Setup Linux Environment 22 | run: | 23 | sudo apt-get install libglu1 24 | - name: Install docs dependencies 25 | run: | 26 | python -m pip install -r docs/requirements.txt 27 | - name: Install package 28 | run: | 29 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 30 | pip install -e . 31 | - name: Build ${{ matrix.doc-type }} documentation 32 | run: sphinx-build -Wnb ${{ matrix.doc-type }} docs/source/ docs/build-${{ matrix.doc-type }}/ 33 | 34 | - name: Prepare documentation artifact 35 | run: | 36 | # Define Path to Upload 37 | if [ "${{ matrix.doc-type }}" = html ]; then echo "art_path=docs/build-html" >> $GITHUB_ENV; fi 38 | if [ "${{ matrix.doc-type }}" = latex ]; then echo "art_path=docs/build-latex/MicroStructPy.pdf" >> $GITHUB_ENV; fi 39 | if [ "${{ matrix.doc-type }}" = epub ]; then echo "art_path=docs/build-epub/MicroStructPy.epub" >> $GITHUB_ENV; fi 40 | 41 | - name: Build PDF (latex only) 42 | if: matrix.doc-type == 'latex' 43 | run: | 44 | # Update apt 45 | sudo apt-get update 46 | 47 | # Install LaTeX packages 48 | # Recommended by Sphinx on their docs page: 49 | # https://www.sphinx-doc.org/en/master/usage/builders/index.html#sphinx.builders.latex.LaTeXBuilder 50 | sudo apt install texlive-latex-recommended 51 | sudo apt install texlive-fonts-recommended 52 | sudo apt install tex-gyre # (if latex_engine left to default) 53 | sudo apt install texlive-latex-extra 54 | sudo apt install latexmk 55 | 56 | # Make 57 | cd docs/build-latex 58 | make 59 | cd - 60 | 61 | - name: Upload artifact 62 | uses: actions/upload-artifact@main 63 | with: 64 | name: microstructpy_${{ matrix.doc-type }}_documentation 65 | path: ${{ env.art_path }} 66 | 67 | -------------------------------------------------------------------------------- /.github/workflows/ossar-analysis.yml: -------------------------------------------------------------------------------- 1 | # This workflow integrates a collection of open source static analysis tools 2 | # with GitHub code scanning. For documentation, or to provide feedback, visit 3 | # https://github.com/github/ossar-action 4 | name: OSSAR 5 | 6 | on: 7 | push: 8 | pull_request: 9 | 10 | jobs: 11 | OSSAR-Scan: 12 | # OSSAR runs on windows-latest. 13 | # ubuntu-latest and macos-latest support coming soon 14 | runs-on: windows-latest 15 | 16 | steps: 17 | # Checkout your code repository to scan 18 | - name: Checkout repository 19 | uses: actions/checkout@main 20 | with: 21 | # We must fetch at least the immediate parents so that if this is 22 | # a pull request then we can checkout the head. 23 | fetch-depth: 2 24 | 25 | # If this run was triggered by a pull request event, then checkout 26 | # the head of the pull request instead of the merge commit. 27 | - run: git checkout HEAD^2 28 | if: ${{ github.event_name == 'pull_request' }} 29 | 30 | # Install dotnet, used by OSSAR 31 | - name: Install .NET 32 | uses: actions/setup-dotnet@main 33 | with: 34 | dotnet-version: '6.0.x' 35 | 36 | # Run open source static analysis tools 37 | - name: Run OSSAR 38 | uses: github/ossar-action@main 39 | id: ossar 40 | 41 | # Upload results to the Security tab 42 | - name: Upload OSSAR results 43 | uses: github/codeql-action/upload-sarif@main 44 | with: 45 | sarif_file: ${{ steps.ossar.outputs.sarifFile }} 46 | -------------------------------------------------------------------------------- /.github/workflows/python_package.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | pytest: 7 | name: pytest for py${{ matrix.python-version }} on ${{ matrix.os }} 8 | 9 | runs-on: ${{ matrix.os }} 10 | strategy: 11 | matrix: 12 | python-version: [3.9, '3.10', '3.11', '3.12'] 13 | os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] 14 | 15 | steps: 16 | - uses: actions/checkout@main 17 | - name: Set up Python ${{ matrix.python-version }} 18 | uses: actions/setup-python@main 19 | with: 20 | python-version: ${{ matrix.python-version }} 21 | 22 | - name: Setup Ubuntu environment 23 | if: startsWith(matrix.os, 'ubuntu') 24 | run: sudo apt-get install -y python3-gmsh libglu1 25 | - name: Setup MacOS environment 26 | if: startsWith(matrix.os, 'macos') 27 | run: brew install gmsh 28 | 29 | - name: Install test dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install setuptools wheel 33 | pip install flake8 pytest==6.2.5 pytest-cov coveralls 34 | - name: Install package requirements 35 | run: pip install -r requirements.txt 36 | - name: Install package 37 | run: pip install -e . 38 | 39 | - name: Lint with flake8 40 | run: | 41 | # stop the build if there are Python syntax errors or undefined names 42 | flake8 src tests setup.py --exclude=__init__.py --count --select=E9,F63,F7,F82 --show-source --statistics 43 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 44 | flake8 src tests setup.py --exclude=__init__.py --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 45 | - name: Test with pytest 46 | run: pytest --cov=src tests/ 47 | 48 | - name: Coveralls 49 | env: 50 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} 51 | COVERALLS_FLAG_NAME: py${{ matrix.python-version }} on ${{ matrix.os }} 52 | run: coveralls 53 | 54 | package-checks: 55 | name: package checks 56 | 57 | runs-on: ubuntu-latest 58 | steps: 59 | - uses: actions/checkout@main 60 | - name: Set up Python 61 | uses: actions/setup-python@main 62 | with: 63 | python-version: '3.x' 64 | - name: Cache pip 65 | uses: actions/cache@main 66 | with: 67 | # This path is specific to Ubuntu 68 | path: ~/.cache/pip 69 | # Look to see if there is a cache hit for the corresponding requirements file 70 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} 71 | restore-keys: | 72 | ${{ runner.os }}-pip- 73 | ${{ runner.os }}- 74 | - name: Install check dependencies 75 | run: | 76 | python -m pip install --upgrade pip 77 | pip install setuptools wheel check-manifest isort twine 78 | - name: Check package import order 79 | run: | 80 | isort --verbose --check-only --diff src tests setup.py 81 | - name: Check package contents 82 | run: | 83 | python setup.py sdist check --strict --metadata 84 | check-manifest 85 | twine check dist/* 86 | -------------------------------------------------------------------------------- /.github/workflows/python_publish.yaml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # 6 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 7 | 8 | name: Upload Python Package 9 | 10 | on: 11 | release: 12 | types: [created] 13 | 14 | jobs: 15 | deploy: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Set up Python 20 | uses: actions/setup-python@v4 21 | with: 22 | python-version: '3.x' 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install build 27 | - name: Build package 28 | run: python -m build 29 | - name: Publish package 30 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 31 | with: 32 | user: __token__ 33 | password: ${{ secrets.PYPI_MICROSTRUCTPY_TOKEN }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # # 3 | # PROJECT-SPECIFIC .GITIGNORE # 4 | # # 5 | ############################################################################### 6 | test_reports/ 7 | .vscode/ 8 | docs/build*/ 9 | 10 | ############################################################################### 11 | # # 12 | # SNIPPET FROM .GITIGNORE GIST # 13 | # # 14 | ############################################################################### 15 | # source: https://gist.github.com/octocat/9257657 16 | 17 | # OS generated files # 18 | ###################### 19 | .DS_Store 20 | .DS_Store? 21 | ._* 22 | .Spotlight-V100 23 | .Trashes 24 | ehthumbs.db 25 | Thumbs.db 26 | 27 | ############################################################################### 28 | # # 29 | # GITHUB'S STANDARD PYTHON .GITIGNORE # 30 | # # 31 | ############################################################################### 32 | 33 | # Byte-compiled / optimized / DLL files 34 | __pycache__/ 35 | *.py[cod] 36 | *$py.class 37 | 38 | # C extensions 39 | *.so 40 | 41 | # Distribution / packaging 42 | .Python 43 | build/ 44 | develop-eggs/ 45 | dist/ 46 | downloads/ 47 | eggs/ 48 | .eggs/ 49 | lib/ 50 | lib64/ 51 | parts/ 52 | sdist/ 53 | var/ 54 | wheels/ 55 | *.egg-info/ 56 | .installed.cfg 57 | *.egg 58 | MANIFEST 59 | 60 | # PyInstaller 61 | # Usually these files are written by a python script from a template 62 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 63 | *.manifest 64 | *.spec 65 | 66 | # Installer logs 67 | pip-log.txt 68 | pip-delete-this-directory.txt 69 | 70 | # Unit test / coverage reports 71 | htmlcov/ 72 | .tox/ 73 | .nox/ 74 | .coverage 75 | .coverage.* 76 | .cache 77 | nosetests.xml 78 | coverage.xml 79 | *.cover 80 | .hypothesis/ 81 | .pytest_cache/ 82 | 83 | # Translations 84 | *.mo 85 | *.pot 86 | 87 | # Django stuff: 88 | *.log 89 | local_settings.py 90 | db.sqlite3 91 | 92 | # Flask stuff: 93 | instance/ 94 | .webassets-cache 95 | 96 | # Scrapy stuff: 97 | .scrapy 98 | 99 | # Sphinx documentation 100 | docs/_build/ 101 | 102 | # PyBuilder 103 | target/ 104 | 105 | # Jupyter Notebook 106 | .ipynb_checkpoints 107 | 108 | # IPython 109 | profile_default/ 110 | ipython_config.py 111 | 112 | # pyenv 113 | .python-version 114 | 115 | # celery beat schedule file 116 | celerybeat-schedule 117 | 118 | # SageMath parsed files 119 | *.sage.py 120 | 121 | # Environments 122 | .env 123 | .venv 124 | env/ 125 | venv/ 126 | ENV/ 127 | env.bak/ 128 | venv.bak/ 129 | 130 | # Spyder project settings 131 | .spyderproject 132 | .spyproject 133 | 134 | # Rope project settings 135 | .ropeproject 136 | 137 | # mkdocs documentation 138 | /site 139 | 140 | # mypy 141 | .mypy_cache/ 142 | .dmypy.json 143 | dmypy.json 144 | 145 | # Pyre type checker 146 | .pyre/ 147 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | formats: all 8 | 9 | # Set the OS, Python version and other tools you might need 10 | build: 11 | os: ubuntu-22.04 12 | tools: 13 | python: "3.8" 14 | apt_packages: 15 | - freeglut3-dev 16 | 17 | # Build documentation in the "docs/" directory with Sphinx 18 | sphinx: 19 | configuration: docs/source/conf.py 20 | 21 | # Optional but recommended, declare the Python requirements required 22 | # to build your documentation 23 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 24 | python: 25 | install: 26 | - requirements: docs/requirements.txt 27 | - method: pip 28 | path: . 29 | -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2024 Georgia Tech Research Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft docs 2 | graft src 3 | graft tests 4 | 5 | include .azure-pipelines.yml 6 | include .bumpversion.cfg 7 | include .coveragerc 8 | include .editorconfig 9 | include .pyup.yml 10 | include .readthedocs.yaml 11 | 12 | include CHANGELOG.rst 13 | include LICENSE.rst 14 | include README.rst 15 | 16 | include tox.ini 17 | include requirements.txt 18 | 19 | prune .github 20 | prune docs/source/auto_examples 21 | prune docs/build* 22 | prune src/*/examples/* 23 | prune src/*.egg-info 24 | prune tests/tmp_out 25 | 26 | exclude coverage.xml 27 | exclude src/*/examples/*.png 28 | include src/microstructpy/examples/aluminum_micro.png 29 | exclude src/*/examples/*.txt 30 | exclude src/*/examples/.gitignore 31 | 32 | global-exclude *.py[cod] __pycache__ *.so *.dylib .DS_Store *.log Icon* 33 | -------------------------------------------------------------------------------- /docs/publications/cmame2020.bib: -------------------------------------------------------------------------------- 1 | @article{Hart2020, 2 | author = {Hart, Kenneth A and Rimoli, Julian J}, 3 | title = {Generation of statistically representative microstructures with direct grain geometry control}, 4 | journal = {Computer Methods in Applied Mechanics and Engineering}, 5 | volume = {370}, 6 | artnum = {113242}, 7 | year = {2020}, 8 | issn = {0045-7825}, 9 | doi = "https://doi.org/10.1016/j.cma.2020.113242", 10 | url = "http://www.sciencedirect.com/science/article/pii/S0045782520304278", 11 | keywords = "Microstructure, Polycrystal, Finite element modeling, Laguerre tessellation, Multi-sphere", 12 | abstract = "Microstructural characteristics play a significant role in the determination of effective properties of materials. Consequently, most numerical tools aimed at predicting such properties require, as a starting point, the availability of geometric representations of such microstructures. In this paper, we introduce a method for generating statistically representative synthetic microstructures for materials involving multiple phases. Our method is based on traditional seed placement and tessellation approaches, with three critical improvements: (i) allowing for controlled seed overlap to better represent microstructural statistics, (ii) multi-sphere representation of 3D ellipsoids to account for arbitrarily elongated grains, and (iii) novel application of the axis aligned bounding box tree structure for accelerated seed placement. Our method recreates a diverse set of microstructural features, including the presence of spherical and non-spherical grain geometries, amorphous phases, and voids. After the method is presented, we proceed with a rigorous numerical analysis demonstrating its ability to reproduce key statistical features of target microstructures. The algorithms presented are freely available and open source through the package MicroStructPy." 13 | } 14 | -------------------------------------------------------------------------------- /docs/publications/swx2020.bib: -------------------------------------------------------------------------------- 1 | @article{Hart2020swX, 2 | title = "MicroStructPy: A statistical microstructure mesh generator in Python", 3 | journal = "SoftwareX", 4 | volume = "12", 5 | pages = "100595", 6 | year = "2020", 7 | issn = "2352-7110", 8 | doi = "https://doi.org/10.1016/j.softx.2020.100595", 9 | url = "http://www.sciencedirect.com/science/article/pii/S2352711020303083", 10 | author = "Kenneth A. Hart and Julian J. Rimoli", 11 | keywords = "Microstructure, Mesh generation, Polycrystal, Laguerre tessellation", 12 | abstract = "MicroStructPy is a statistical microstructure mesh generator written in Python. This software package includes classes and methods to generate a mesh by (i) creating a list of grain seed geometries, (ii) packing them into a domain, (iii) performing a Laguerre tessellation of the seed geometries, and (iv) performing quality unstructured meshing. Results can be visualized and compared with the specified microstructural properties. MicroStructPy accurately reproduces 2D and 3D polycrystalline microstructures with arbitrary number of phases, volume fractions and distributions, each including the possibility of elongated grains. It can also generate meshes for amorphous phases and porous materials. Meshes are suitable for direct numerical simulation, a prevalent technique in computational mechanics of materials and geomechanics. The package contains extensive documentation, including guides and demonstrations to facilitate adoption for new users." 13 | } 14 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | gmsh==4.11.1 2 | matplotlib>=3.7.3 3 | numpy>=1.24.4 4 | pybind11==2.4.3 5 | requests>=2.32.0 # not directly required, pinned by Snyk to avoid a vulnerability 6 | setuptools>=70.0.0 7 | sphinx==5.3.0 8 | sphinx-gallery==0.8.1 9 | urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability 10 | zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability 11 | -------------------------------------------------------------------------------- /docs/source/.gitignore: -------------------------------------------------------------------------------- 1 | auto_examples/ -------------------------------------------------------------------------------- /docs/source/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/favicon.ico -------------------------------------------------------------------------------- /docs/source/_static/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 27 | 35 | 43 | 51 | 59 | 63 | 64 | -------------------------------------------------------------------------------- /docs/source/_static/logo/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | #FFFFFF 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/source/_static/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_114x114.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_120x120.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_128x128.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_144x144.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_180x180.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_192x192.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_196x196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_196x196.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_228x228.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_228x228.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_32x32.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_57x57.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_72x72.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_76x76.png -------------------------------------------------------------------------------- /docs/source/_static/logo/logo_96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/logo_96x96.png -------------------------------------------------------------------------------- /docs/source/_static/logo/pad_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/pad_logo.png -------------------------------------------------------------------------------- /docs/source/_static/logo/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/docs/source/_static/logo/social.png -------------------------------------------------------------------------------- /docs/source/_static/pypi.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 36 | 43 | 47 | 48 | -------------------------------------------------------------------------------- /docs/source/_static/rtd.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 24 | 31 | 35 | 36 | -------------------------------------------------------------------------------- /docs/source/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {%- block extrahead %} 3 | {%- if render_sidebar %} 4 | 5 | {{ super() }} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {% endif %} 66 | {%- endblock %} 67 | -------------------------------------------------------------------------------- /docs/source/api/cli.rst: -------------------------------------------------------------------------------- 1 | microstructpy.cli 2 | ================= 3 | 4 | .. automodule:: microstructpy.cli 5 | :members: 6 | :undoc-members: -------------------------------------------------------------------------------- /docs/source/api/geometry/box.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_box: 2 | 3 | .. currentmodule:: microstructpy.geometry 4 | 5 | microstructpy.geometry.Box 6 | ========================== 7 | 8 | .. autoclass:: Box 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/geometry/circle.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_circle: 2 | 3 | .. currentmodule:: microstructpy.geometry 4 | 5 | microstructpy.geometry.Circle 6 | ============================= 7 | 8 | .. autoclass:: Circle 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/geometry/cube.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_cube: 2 | 3 | .. currentmodule:: microstructpy.geometry 4 | 5 | microstructpy.geometry.Cube 6 | =========================== 7 | 8 | .. autoclass:: Cube 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | -------------------------------------------------------------------------------- /docs/source/api/geometry/ellipse.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_ellipse: 2 | 3 | .. currentmodule:: microstructpy.geometry 4 | 5 | microstructpy.geometry.Ellipse 6 | ============================== 7 | 8 | .. autoclass:: Ellipse 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/geometry/ellipsoid.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_ellipsoid: 2 | 3 | .. currentmodule:: microstructpy.geometry 4 | 5 | microstructpy.geometry.Ellipsoid 6 | ================================ 7 | 8 | .. autoclass:: Ellipsoid 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/geometry/factory.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_factory: 2 | 3 | .. currentmodule:: microstructpy.geometry 4 | 5 | microstructpy.geometry.factory 6 | ============================== 7 | 8 | .. automethod:: microstructpy.geometry.factory -------------------------------------------------------------------------------- /docs/source/api/geometry/index.rst: -------------------------------------------------------------------------------- 1 | microstructpy.geometry 2 | ====================== 3 | 4 | .. automodule:: microstructpy.geometry 5 | 6 | The geometry module contains classes for several 2D and 3D geometries. 7 | The module also contains some N-D geometries, which are inherited by the 8 | 2D and 3D geometries. 9 | 10 | **2D Geometries** 11 | 12 | * :ref:`api_geometry_circle` † ‡ 13 | * :ref:`api_geometry_ellipse` † ‡ 14 | * :ref:`api_geometry_rectangle` † ‡ 15 | * :ref:`api_geometry_square` † ‡ 16 | 17 | **3D Geometries** 18 | 19 | * :ref:`api_geometry_box` ‡ 20 | * :ref:`api_geometry_cube` ‡ 21 | * :ref:`api_geometry_ellipsoid` † 22 | * :ref:`api_geometry_sphere` † 23 | 24 | **ND Geometries** 25 | 26 | * :ref:`api_geometry_n_box` 27 | * :ref:`api_geometry_n_sphere` 28 | 29 | †: These classes may be used to define seed particles. 30 | 31 | ‡: These classes may be used to define the microstructure domain. 32 | 33 | To assist with creating geometries, a factory method is included in the module: 34 | 35 | * :ref:`api_geometry_factory` 36 | 37 | .. only:: html 38 | 39 | **Module Contents** 40 | 41 | .. toctree:: 42 | 43 | box 44 | circle 45 | cube 46 | ellipse 47 | ellipsoid 48 | n_box 49 | n_sphere 50 | rectangle 51 | sphere 52 | square 53 | factory 54 | -------------------------------------------------------------------------------- /docs/source/api/geometry/n_box.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_n_box: 2 | 3 | .. currentmodule:: microstructpy.geometry.n_box 4 | 5 | microstructpy.geometry.n_box.NBox 6 | ================================= 7 | 8 | .. autoclass:: NBox 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/geometry/n_sphere.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_n_sphere: 2 | 3 | .. currentmodule:: microstructpy.geometry.n_sphere 4 | 5 | microstructpy.geometry.n_sphere.NSphere 6 | ======================================= 7 | 8 | .. autoclass:: NSphere 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/geometry/rectangle.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_rectangle: 2 | 3 | .. currentmodule:: microstructpy.geometry 4 | 5 | microstructpy.geometry.Rectangle 6 | ================================ 7 | 8 | .. autoclass:: Rectangle 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/geometry/sphere.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_sphere: 2 | 3 | .. currentmodule:: microstructpy.geometry 4 | 5 | microstructpy.geometry.Sphere 6 | ============================= 7 | 8 | .. autoclass:: Sphere 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/geometry/square.rst: -------------------------------------------------------------------------------- 1 | .. _api_geometry_square: 2 | 3 | .. currentmodule:: microstructpy.geometry 4 | 5 | microstructpy.geometry.Square 6 | ============================= 7 | 8 | .. autoclass:: Square 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | -------------------------------------------------------------------------------- /docs/source/api/index.rst: -------------------------------------------------------------------------------- 1 | .. _api-index: 2 | 3 | API 4 | === 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | cli 10 | geometry/index 11 | meshing/index 12 | seeding/index 13 | verification 14 | -------------------------------------------------------------------------------- /docs/source/api/meshing/index.rst: -------------------------------------------------------------------------------- 1 | microstructpy.meshing 2 | ===================== 3 | 4 | .. automodule:: microstructpy.meshing 5 | 6 | The meshing module contains three mesh classes, 7 | the :class:`.PolyMesh`, the :class:`.RasterMesh` and the :class:`.TriMesh`. 8 | The polygonal mesh contains a 2D or 3D tessellation of the microstructure 9 | domain, while the raster aand triangular meshes are more suitable for 10 | direct numerical simulation (finite element analysis). 11 | 12 | .. only:: html 13 | 14 | **Module Contents** 15 | 16 | .. toctree:: 17 | 18 | polymesh 19 | rastermesh 20 | trimesh -------------------------------------------------------------------------------- /docs/source/api/meshing/polymesh.rst: -------------------------------------------------------------------------------- 1 | .. _api_meshing_polymesh: 2 | 3 | .. currentmodule:: microstructpy.meshing 4 | 5 | microstructpy.meshing.PolyMesh 6 | ============================== 7 | 8 | .. autoclass:: PolyMesh 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: 13 | -------------------------------------------------------------------------------- /docs/source/api/meshing/rastermesh.rst: -------------------------------------------------------------------------------- 1 | .. _api_meshing_rastermesh: 2 | 3 | .. currentmodule:: microstructpy.meshing 4 | 5 | microstructpy.meshing.RasterMesh 6 | ================================ 7 | 8 | .. autoclass:: RasterMesh 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | -------------------------------------------------------------------------------- /docs/source/api/meshing/trimesh.rst: -------------------------------------------------------------------------------- 1 | .. _api_meshing_trimesh: 2 | 3 | .. currentmodule:: microstructpy.meshing 4 | 5 | microstructpy.meshing.TriMesh 6 | ============================= 7 | 8 | .. autoclass:: TriMesh 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: 13 | -------------------------------------------------------------------------------- /docs/source/api/seeding/index.rst: -------------------------------------------------------------------------------- 1 | microstructpy.seeding 2 | ===================== 3 | 4 | .. automodule:: microstructpy.seeding 5 | 6 | The seeding module contains two classes: :class:`.Seed` and :class:`.SeedList`. 7 | The single :class:`.Seed` contains the geometry, phase number, and position 8 | of a seed, while a :class:`.SeedList` functions much like a list of 9 | :class:`.Seed` instances, but with more methods. 10 | 11 | 12 | .. only:: html 13 | 14 | **Module Contents** 15 | 16 | .. toctree:: 17 | 18 | seed 19 | seedlist -------------------------------------------------------------------------------- /docs/source/api/seeding/seed.rst: -------------------------------------------------------------------------------- 1 | .. _api_seeding_seed: 2 | 3 | .. currentmodule:: microstructpy.seeding 4 | 5 | microstructpy.seeding.Seed 6 | ========================== 7 | 8 | .. autoclass:: Seed 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/seeding/seedlist.rst: -------------------------------------------------------------------------------- 1 | .. _api_seeding_seedlist: 2 | 3 | .. currentmodule:: microstructpy.seeding 4 | 5 | microstructpy.seeding.SeedList 6 | ============================== 7 | 8 | .. autoclass:: SeedList 9 | :members: 10 | :undoc-members: 11 | :inherited-members: 12 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/api/verification.rst: -------------------------------------------------------------------------------- 1 | microstructpy.verification 2 | ========================== 3 | 4 | .. automodule:: microstructpy.verification 5 | :members: 6 | :undoc-members: -------------------------------------------------------------------------------- /docs/source/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../CHANGELOG.rst 2 | -------------------------------------------------------------------------------- /docs/source/cli/index.rst: -------------------------------------------------------------------------------- 1 | .. _cli: 2 | 3 | ================== 4 | Command Line Guide 5 | ================== 6 | 7 | .. only:: html 8 | 9 | .. include:: introduction.rst 10 | :start-after: .. cli-start 11 | :end-before: .. cli-end 12 | 13 | .. rubric:: Guide Contents 14 | 15 | .. toctree:: 16 | :maxdepth: 3 17 | 18 | introduction 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/source/cli/introduction.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Introduction 3 | ============ 4 | 5 | Using the Command Line Interface 6 | -------------------------------- 7 | 8 | .. cli-start 9 | 10 | The command line interface (CLI) for this package is ``microstructpy``. 11 | This command accepts the names of user-generated files and demonstration files. 12 | Multiple filenames can be specified. 13 | 14 | To run demos, you can specify a particular demo file or to run all of them:: 15 | 16 | microstructpy --demo=minimal.xml 17 | microstructpy --demo=all 18 | 19 | Demo files are copied to the current working directory and then executed. 20 | Running all of the demonstration files may take several minutes. 21 | 22 | User-generated input files can be run in a number of ways:: 23 | 24 | microstructpy /path/to/my/input_file.xml 25 | microstructpy input_1.xml input_2.xml input_3.xml 26 | microstructpy input_*.xml 27 | 28 | Both relative and absolute filepaths are acceptable. 29 | 30 | The following pages describe in detail the various uses and options for the 31 | material, domain, and settings fields of a MicroStructPy input file. 32 | 33 | .. cli-end 34 | 35 | Command Line Procedure 36 | ---------------------- 37 | 38 | The following tasks are performed by the CLI: 39 | 40 | 1. Make the output directory, if necessary 41 | 2. **Create a list of unpositioned seeds** 42 | 3. **Position the seeds in the domain** 43 | 4. Save the seeds in a text file 44 | 5. Save a plot of the seeds to an image file 45 | 6. **Create a polygon mesh from the seeds** 46 | 7. Save the mesh to the output directory 47 | 8. Save a plot of the mesh to the output directory 48 | 9. **Create an unstructured (triangular or tetrahedral) mesh** 49 | 10. Save the unstructured mesh 50 | 11. Save a plot of the unstructured mesh 51 | 12. (optional) Verify the output mesh against the input file. 52 | 53 | Intermediate results are saved in steps 4, 7, and 10 to give the option of 54 | restarting the procedure. 55 | The format of the output files can be specified in the input file 56 | (e.g. PNG and/or PDF plots). 57 | 58 | Example Input File 59 | ------------------ 60 | 61 | Input files for MicroStructPy must be in XML format. 62 | The three fields of the input file that MicroStructPy looks for are: 63 | ````, ````, and ```` (optional). 64 | For example: 65 | 66 | .. literalinclude:: ../../../src/microstructpy/examples/minimal_paired.xml 67 | :language: xml 68 | 69 | 70 | This will create a microstructure with approximately circular grains that 71 | fill a domain that is 11x larger and color them according to the colormap 72 | Paired. 73 | 74 | .. note:: 75 | 76 | XML fields that are not recognized by MicroStructPy will be ignored by the 77 | software. For example, material properties or notes can be included in the 78 | input file without affecting program execution. 79 | 80 | .. note:: 81 | 82 | The order of fields in the XML input file is not strictly important, 83 | since the file is converted into a Python dictionary. 84 | When fields are repeated, such as including multiple materials, the order 85 | is preserved. 86 | 87 | 88 | Including References to Other Input Files 89 | ----------------------------------------- 90 | 91 | The input file can optionally *include* references to other input files. 92 | For example if the file ``materials.xml`` contains: 93 | 94 | .. code-block:: xml 95 | 96 | 97 | 98 | circle 99 | 0.1 100 | 101 | 102 | 103 | and another file, ``domain_1.xml``, contains: 104 | 105 | .. code-block:: xml 106 | 107 | 108 | materials.xml 109 | 110 | square 111 | 10 112 | 113 | 114 | 115 | then MicroStructPy will read the contents of ``materials.xml`` when 116 | ``microstructpy domain_1.xml`` is called. This functionality can allows multiple 117 | input files to reference the same material properties. For example, a mesh 118 | convergence study could keep the materials and domain definitions in a single 119 | file, then the input files for each mesh size would contain the run settings 120 | and a reference to the definitions file. 121 | 122 | This way, if a parameter such as the grain size distribution needs to be 123 | updated, it only needs to be changed in a single file. 124 | 125 | Advanced Usage 126 | ++++++++++++++ 127 | 128 | The ```` tag can be included at any heirarchical level of the 129 | input file. It can also be nested, with ```` tags in the file being 130 | included. For example, if the file ``fine_grained.xml`` contains: 131 | 132 | .. code-block:: xml 133 | 134 | 135 | circle 136 | 0.1 137 | 138 | 139 | and the file ``materials.xml`` contains: 140 | 141 | 142 | 143 | .. code-block:: xml 144 | 145 | 146 | 147 | Fine 1 148 | fine_grained.xml 149 | 150 | 151 | 152 | Fine 2 153 | fine_grained.xml 154 | 155 | 156 | 157 | Coarse 158 | circle 159 | 0.3 160 | 161 | 162 | 163 | and the file ``input.xml`` contains: 164 | 165 | .. code-block:: xml 166 | 167 | 168 | materials.xml 169 | 170 | square 171 | 20 172 | 173 | 174 | 175 | then running ``microstructpy input.xml`` would be equivalent to running this 176 | file: 177 | 178 | .. code-block:: xml 179 | 180 | 181 | 182 | Fine 1 183 | circle 184 | 0.1 185 | 186 | 187 | 188 | Fine 2 189 | circle 190 | 0.1 191 | 192 | 193 | 194 | Coarse 195 | circle 196 | 0.3 197 | 198 | 199 | 200 | square 201 | 20 202 | 203 | 204 | 205 | 206 | The ```` tag can reduce file sizes and the amount of copy/paste for 207 | microstructures with multiple materials of the same size distribution, 208 | or multiple runs with the same material. 209 | -------------------------------------------------------------------------------- /docs/source/contents.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | MicroStructPy Contents 4 | ====================== 5 | 6 | .. toctree:: 7 | :maxdepth: 3 8 | 9 | welcome 10 | getting_started 11 | examples/index 12 | cli/index 13 | package_guide 14 | file_formats 15 | api/index 16 | troubleshooting 17 | -------------------------------------------------------------------------------- /docs/source/docutils.conf: -------------------------------------------------------------------------------- 1 | [restructuredtext parser] 2 | tab_width: 4 -------------------------------------------------------------------------------- /docs/source/examples/cli/basalt.rst: -------------------------------------------------------------------------------- 1 | .. _ex_basalt: 2 | 3 | =============== 4 | Picritic Basalt 5 | =============== 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``basalt_circle.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=basalt_circle.xml 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/basalt_circle.xml 18 | :language: xml 19 | 20 | 21 | Material 1 - Plagioclase 22 | ======================== 23 | 24 | Plagioclase composes approximately 45% of this picritic basalt sample. 25 | It is an *aphanitic* component, meaning fine-grained, and follows a custom 26 | size distribution. 27 | 28 | Material 2 - Olivine 29 | ==================== 30 | 31 | Olivine composes approximately 19% of this picritic basalt sample. 32 | There are large *phenocrysts* of olivine in picritic basalt, so the crystals 33 | are generally larger than the other components and have a non-circular shape. 34 | The orientation of the phenocrysts is uniform random, with the aspect ratio 35 | varying from 1 to 3 uniformly. 36 | 37 | Materials 3-8 38 | ============= 39 | 40 | Diopside, hypersthene, magnetite, chromite, ilmenite, and apatie compose 41 | approximately 36% of this picritic basalt sample. 42 | They are *aphanitic* components, meaning fine-grained, and follow a custom 43 | size distribution. 44 | 45 | Domain Geometry 46 | =============== 47 | 48 | These materials fill a circular domain with a diameter of 30 mm. 49 | 50 | 51 | Settings 52 | ======== 53 | 54 | The function will output plots of the microstructure process and those plots 55 | are saved as PNGs. 56 | They are saved in a folder named ``basalt_circle``, in the current directory 57 | (i.e ``./basalt_circle``). 58 | 59 | The axes are turned off in these plots, creating PNG files with 60 | minimal whitespace. 61 | 62 | Mesh controls are introducted to increase grid resolution, particularly at the 63 | grain boundaries. 64 | 65 | 66 | Output Files 67 | ============ 68 | 69 | The three plots that this file generates are the seeding, the polygon mesh, 70 | and the triangular mesh. 71 | These three plots are shown in 72 | :numref:`f_ex_basalt_seeds` - :numref:`f_ex_basalt_tri`. 73 | 74 | .. _f_ex_basalt_seeds: 75 | .. figure:: ../../../../src/microstructpy/examples/basalt_circle/seeds.png 76 | :alt: Seed geometries. 77 | 78 | Picritic basalt example - seed geometries 79 | 80 | .. _f_ex_basalt_poly: 81 | .. figure:: ../../../../src/microstructpy/examples/basalt_circle/polymesh.png 82 | :alt: Polygonal mesh. 83 | 84 | Picritic basalt example - polygonal mesh 85 | 86 | .. _f_ex_basalt_tri: 87 | .. figure:: ../../../../src/microstructpy/examples/basalt_circle/trimesh.png 88 | :alt: Triangular mesh. 89 | 90 | Picritic basalt example - triangular mesh 91 | 92 | With the ```` flag set to ``True``, verification plots are 93 | generated by MicroStructPy. 94 | The grain size distribution comparison is given in :numref:`f_ex_basalt_verif`. 95 | 96 | .. _f_ex_basalt_verif: 97 | .. figure:: ../../../../src/microstructpy/examples/basalt_circle/verification/size_cdf.png 98 | :alt: Comparing input and output CSDs. 99 | 100 | Picritic basalt example - input and output crystal size 101 | distributions (CSDs). 102 | 103 | Comparing the input and output distributions for olivine, it is clear that this 104 | microstructure is not statistically representative. 105 | A larger diameter for the domain would contain more grains of olivine, which 106 | would add more fidelity to the size CDF curve. 107 | 108 | -------------------------------------------------------------------------------- /docs/source/examples/cli/colormap.rst: -------------------------------------------------------------------------------- 1 | .. _ex_colormap: 2 | 3 | ======== 4 | Colormap 5 | ======== 6 | 7 | In this example, a 3D microstructure is generated with grains colored by their 8 | seed number, rather than the material. 9 | 10 | XML Input File 11 | ============== 12 | 13 | The basename for this input file is ``colormap.xml``. 14 | The file can be run using this command:: 15 | 16 | microstructpy --demo=colormap.xml 17 | 18 | The full text of the script is: 19 | 20 | .. literalinclude:: ../../../../src/microstructpy/examples/colormap.xml 21 | :language: xml 22 | 23 | 24 | Materials 25 | ========= 26 | 27 | There is a single material with grain sizes ranging from 1 to 3 on a 28 | uniform distribution. 29 | 30 | Domain 31 | ====== 32 | 33 | The domain of the microstructure is a :class:`.Cube`. 34 | The cube's center is at the origin and its side length is 15. 35 | 36 | Settings 37 | ======== 38 | 39 | The output directory is ``./colormap``, which contains the text files 40 | and PNG plots of the microstructure. 41 | The minimum dihedral angle for the mesh elements is set to 15 degrees to 42 | ensure mesh quality. 43 | 44 | The ```` option indicates how to color seeds in the output plots. 45 | There are three values available for this option: "material", "seed number", 46 | and "material number". 47 | The "material" value will use colors specified in each ```` field. 48 | The "seed number" value will use the seed numbers as values for a colormap. 49 | Similarly, "material number" will use the material number in a colormap. 50 | 51 | The ```` option indicates which colormap should be used in the output 52 | plot. 53 | The default colormap is "viridis", which is also the default for matplotlib. 54 | A complete listing of available colormaps is available on the matplotlib 55 | `Choosing Colormaps in Matplotlib`_ webpage. 56 | 57 | Output Files 58 | ============ 59 | 60 | The three plots that this file generates are the seeding, the polygon mesh, 61 | and the triangular mesh. 62 | These three plots are shown in :numref:`f_ex_colormap_seeds` - 63 | :numref:`f_ex_colormap_tri`. 64 | 65 | .. _f_ex_colormap_seeds: 66 | .. figure:: ../../../../src/microstructpy/examples/colormap/seeds.png 67 | :alt: Seed geometries. 68 | 69 | Colormap example - seed geometries. 70 | 71 | .. _f_ex_colormap_poly: 72 | .. figure:: ../../../../src/microstructpy/examples/colormap/polymesh.png 73 | :alt: Polygonal mesh. 74 | 75 | Colormap example - polygonal mesh. 76 | 77 | .. _f_ex_colormap_tri: 78 | .. figure:: ../../../../src/microstructpy/examples/colormap/trimesh.png 79 | :alt: Triangular mesh. 80 | 81 | Colormap example - triangular mesh. 82 | 83 | 84 | .. _`Choosing Colormaps in Matplotlib`: https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html 85 | -------------------------------------------------------------------------------- /docs/source/examples/cli/elliptical_grains.rst: -------------------------------------------------------------------------------- 1 | .. _ex_elliptical_grains: 2 | 3 | ================= 4 | Elliptical Grains 5 | ================= 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``elliptical_grains.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=elliptical_grains.xml 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/elliptical_grains.xml 18 | :language: xml 19 | 20 | 21 | Materials 22 | ========= 23 | 24 | There are five materials, represented in equal proportions. 25 | The first material consists of ellipses and the semi-major axes are 26 | uniformly distributed, :math:`A \sim U(0.20, 0.75)`. 27 | The semi-minor axes are fixed at 0.05, meaning the aspect ratio of these 28 | seeds are 4-15. 29 | The orientation angles of the ellipses are uniform random in distribution from 30 | 0 to 20 degrees counterclockwise from the +x axis. 31 | 32 | The remaining four materials are all the same, with lognormal grain area 33 | distributions. 34 | The only difference among these materials is the color, which was done for 35 | visual effect. 36 | 37 | 38 | Domain Geometry 39 | =============== 40 | 41 | The domain of the microstructure is a rectangle with side lengths 2.4 in the 42 | x-direction and 1.2 in the y-direction. 43 | The domain is centered on the origin, though the position of the domain is 44 | not relevant considering that the plot axes are switched off. 45 | 46 | 47 | Settings 48 | ======== 49 | 50 | The aspect ratio of elements in the triangular mesh is controlled 51 | by setting the minimum interior angle for the elements at 20 degrees, 52 | the maximum element volume to 0.001, and the maximum edge length at grain 53 | boundaries to 0.01. 54 | 55 | The function will output only plots of the microstructure process 56 | (no text files), and those plots are saved as PNGs. 57 | They are saved in a folder named ``elliptical_grains``, in the current directory 58 | (i.e ``./elliptical_grains``). 59 | 60 | The axes are turned off in these plots, creating PNG files with 61 | minimal whitespace. 62 | 63 | Finally, the linewiths in the seeds plot, polygonal mesh plot, and the 64 | triangular mesh plot are 0.5, 0.5, 0.1 respectively. 65 | 66 | 67 | Output Files 68 | ============ 69 | 70 | The three plots that this file generates are the seeding, the polygon mesh, 71 | and the triangular mesh. 72 | These three plots are shown in :numref:`f_ex_ell_seeds` - 73 | :numref:`f_ex_ell_tri`. 74 | 75 | .. _f_ex_ell_seeds: 76 | .. figure:: ../../../../src/microstructpy/examples/elliptical_grains/seeds.png 77 | :alt: Seed geometries. 78 | 79 | Elliptical grain example - seed geometries. 80 | 81 | .. _f_ex_ell_poly: 82 | .. figure:: ../../../../src/microstructpy/examples/elliptical_grains/polymesh.png 83 | :alt: Polygonal mesh. 84 | 85 | Elliptical grain example - polygonal mesh. 86 | 87 | .. _f_ex_ell_tri: 88 | .. figure:: ../../../../src/microstructpy/examples/elliptical_grains/trimesh.png 89 | :alt: Triangular mesh. 90 | 91 | Elliptical grain example - triangular mesh. 92 | 93 | -------------------------------------------------------------------------------- /docs/source/examples/cli/minimal.rst: -------------------------------------------------------------------------------- 1 | .. _ex_minimal: 2 | 3 | =============== 4 | Minimal Example 5 | =============== 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``minimal_paired.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=minimal_paired.xml 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/minimal_paired.xml 18 | :language: xml 19 | 20 | 21 | Material 22 | ======== 23 | 24 | There is only one material, with a constant size of 0.09. 25 | 26 | Domain Geometry 27 | =============== 28 | 29 | The material fills a square domain. 30 | The default side length is 1, meaning the domain is greater than 10x larger 31 | than the grains. 32 | 33 | 34 | Settings 35 | ======== 36 | 37 | The function will output plots of the microstructure process and those plots 38 | are saved as PNGs. 39 | They are saved in a folder named ``minimal``, in the current directory 40 | (i.e ``./minimal``). 41 | 42 | The axes are turned off in these plots, creating PNG files with 43 | minimal whitespace. 44 | 45 | This example also demonstrates how to use gmsh to generate a mesh, using the 46 | ```` and ```` tags in the input file. 47 | 48 | Finally, the seeds and grains are colored by their seed number, not by 49 | material. 50 | 51 | 52 | Output Files 53 | ============ 54 | 55 | The three plots that this file generates are the seeding, the polygon mesh, 56 | and the triangular mesh. 57 | These three plots are shown in :numref:`f_ex_min_seeds` - 58 | :numref:`f_ex_min_tri`. 59 | 60 | .. _f_ex_min_seeds: 61 | .. figure:: ../../../../src/microstructpy/examples/minimal/seeds.png 62 | :alt: Seed geometries. 63 | 64 | Minimal example - seed geometries. 65 | 66 | .. _f_ex_min_poly: 67 | .. figure:: ../../../../src/microstructpy/examples/minimal/polymesh.png 68 | :alt: Polygonal mesh. 69 | 70 | Minimal example - polygonal mesh. 71 | 72 | .. _f_ex_min_tri: 73 | .. figure:: ../../../../src/microstructpy/examples/minimal/trimesh.png 74 | :alt: Triangular mesh. 75 | 76 | Minimal example - triangular mesh. -------------------------------------------------------------------------------- /docs/source/examples/cli/two_phase_3d.rst: -------------------------------------------------------------------------------- 1 | .. _ex_2phase_3d: 2 | 3 | ==================== 4 | Two Phase 3D Example 5 | ==================== 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``two_phase_3D.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=two_phase_3D.xml 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/two_phase_3D.xml 18 | :language: xml 19 | 20 | 21 | Materials 22 | ========= 23 | 24 | The first material makes up 25% of the volume, with a lognormal grain volume 25 | distribution. 26 | 27 | The second material makes up 75% of the volume, with an independent grain 28 | volume distribution. 29 | 30 | Domain Geometry 31 | =============== 32 | 33 | These two materials fill a cube domain of side length 7. 34 | 35 | 36 | Settings 37 | ======== 38 | 39 | The function will output plots of the microstructure process and those plots 40 | are saved as PNGs. 41 | They are saved in a folder named ``two_phase_3D``, in the current directory 42 | (i.e ``./two_phase_3D``). 43 | 44 | The line width of the output plots is reduced to 0.2, to make them more 45 | visible. 46 | 47 | 48 | Output Files 49 | ============ 50 | 51 | The three plots that this file generates are the seeding, the polygon mesh, 52 | and the triangular mesh. 53 | These three plots are shown in :numref:`f_ex_2p3d_seeds` - 54 | :numref:`f_ex_2p3d_tri`. 55 | 56 | .. _f_ex_2p3d_seeds: 57 | .. figure:: ../../../../src/microstructpy/examples/two_phase_3D/seeds.png 58 | :alt: Seed geometries. 59 | 60 | Two phase 3D example - seed geometries. 61 | 62 | .. _f_ex_2p3d_poly: 63 | .. figure:: ../../../../src/microstructpy/examples/two_phase_3D/polymesh.png 64 | :alt: Polygonal mesh. 65 | 66 | Two phase 3D example - polygonal mesh. 67 | 68 | .. _f_ex_2p3d_tri: 69 | .. figure:: ../../../../src/microstructpy/examples/two_phase_3D/trimesh.png 70 | :alt: Triangular mesh. 71 | 72 | Two phase 3D example - triangular mesh. 73 | -------------------------------------------------------------------------------- /docs/source/examples/index.rst: -------------------------------------------------------------------------------- 1 | .. _examples_page: 2 | 3 | Examples 4 | ========= 5 | 6 | The following contains examples of MicroStructPy. 7 | These examples include an introduction to the XML input files, 8 | some more advanced input files used on the CLI, 9 | and scripts that use the Python package. 10 | 11 | .. _intro_examples: 12 | 13 | Input File Introduction 14 | ----------------------- 15 | 16 | .. toctree:: 17 | :maxdepth: 1 18 | :numbered: 19 | 20 | intro/intro_1 21 | intro/intro_2 22 | intro/intro_3 23 | intro/intro_4 24 | intro/intro_5 25 | intro/intro_6 26 | 27 | .. only:: html 28 | 29 | .. image:: ../../../src/microstructpy/examples/intro_1_basic/trimesh.png 30 | :alt: Triangular mesh from first intro example. 31 | :width: 32% 32 | :target: intro/intro_1.html 33 | 34 | 35 | .. image:: ../../../src/microstructpy/examples/intro_2_quality/trimesh.png 36 | :alt: Triangular mesh from second intro example. 37 | :width: 32% 38 | :target: intro/intro_2.html 39 | 40 | 41 | .. image:: ../../../src/microstructpy/examples/intro_3_size_shape/trimesh.png 42 | :alt: Triangular mesh from third intro example. 43 | :width: 32% 44 | :target: intro/intro_3.html 45 | 46 | 47 | .. image:: ../../../src/microstructpy/examples/intro_4_oriented/trimesh.png 48 | :alt: Triangular mesh from fourth intro example. 49 | :width: 32% 50 | :target: intro/intro_4.html 51 | 52 | 53 | .. image:: ../../../src/microstructpy/examples/intro_5_plotting/trimesh.png 54 | :alt: Triangular mesh from fifth intro example. 55 | :width: 32% 56 | :target: intro/intro_5.html 57 | 58 | 59 | .. image:: ../../../src/microstructpy/examples/intro_6_culmination/trimesh.png 60 | :alt: Triangular mesh from sixth intro example. 61 | :width: 32% 62 | :target: intro/intro_6.html 63 | 64 | 65 | .. _cli_examples: 66 | 67 | CLI Examples 68 | ------------ 69 | 70 | .. toctree:: 71 | :maxdepth: 1 72 | 73 | cli/elliptical_grains 74 | cli/minimal 75 | cli/basalt 76 | cli/two_phase_3d 77 | cli/colormap 78 | 79 | .. only:: html 80 | 81 | .. image:: ../../../src/microstructpy/examples/elliptical_grains/trimesh.png 82 | :alt: Triangular mesh with elliptical grains. 83 | :width: 64% 84 | :target: cli/elliptical_grains.html 85 | 86 | .. image:: ../../../src/microstructpy/examples/minimal/polymesh.png 87 | :alt: Polygonal mesh from minimal example. 88 | :width: 32% 89 | :target: cli/minimal.html 90 | 91 | .. image:: ../../../src/microstructpy/examples/basalt_circle/trimesh.png 92 | :alt: Triangular mesh from basalt example. 93 | :width: 32% 94 | :target: cli/basalt.html 95 | 96 | .. image:: ../../../src/microstructpy/examples/two_phase_3D/polymesh.png 97 | :alt: Polygonal mesh from 3D two phase example. 98 | :width: 32% 99 | :target: cli/two_phase_3d.html 100 | 101 | .. image:: ../../../src/microstructpy/examples/colormap/trimesh.png 102 | :alt: Triangular mesh from 3D colormap example. 103 | :width: 32% 104 | :target: cli/colormap.html 105 | 106 | 107 | 108 | .. _package_examples: 109 | 110 | Python Package Examples 111 | ----------------------- 112 | 113 | .. toctree:: 114 | :maxdepth: 1 115 | 116 | package/standard_voronoi 117 | package/uniform_seeding 118 | package/foam 119 | package/logo 120 | package/grain_neighborhoods 121 | package/from_image 122 | package/mesh_process 123 | 124 | .. only:: html 125 | 126 | .. image:: ../../../src/microstructpy/examples/standard_voronoi/voronoi_diagram.png 127 | :alt: Standard Voronoi diagram. 128 | :height: 210px 129 | :target: package/standard_voronoi.html 130 | 131 | .. image:: ../../../src/microstructpy/examples/uniform_seeding/voronoi_diagram.png 132 | :alt: Voronoi diagram with uniformly-spaced seeds, colored by area. 133 | :height: 210px 134 | :target: package/uniform_seeding.html 135 | 136 | .. image:: ../../../src/microstructpy/examples/foam/trimesh.png 137 | :alt: Mesh of foam microstructure. 138 | :height: 210px 139 | :target: package/foam.html 140 | 141 | .. image:: ../../../src/microstructpy/examples/logo/logo.png 142 | :alt: MicroStructPy logo. 143 | :height: 210px 144 | :target: package/logo.html 145 | 146 | .. image:: ../../../src/microstructpy/examples/grain_neighborhoods/trimesh.png 147 | :alt: Triangular mesh of microstructure with seed neighborhoods. 148 | :height: 210px 149 | :target: package/grain_neighborhoods.html 150 | 151 | .. image:: ../../../src/microstructpy/examples/from_image/trimesh.png 152 | :alt: Triangular mesh of aluminum microstructure. 153 | :height: 210px 154 | :target: package/from_image.html 155 | 156 | .. image:: ../../../src/microstructpy/examples/docs_banner/banner.png 157 | :alt: Microstructure meshing process.. 158 | :target: package/mesh_process.html 159 | -------------------------------------------------------------------------------- /docs/source/examples/intro/intro_1.rst: -------------------------------------------------------------------------------- 1 | .. _ex_1_basic: 2 | 3 | ============= 4 | Basic Example 5 | ============= 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``intro_1_basic.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=intro_1_basic.xml 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/intro_1_basic.xml 18 | :language: xml 19 | 20 | 21 | Materials 22 | ========= 23 | 24 | There are two materials, in a 2:1 ratio based on volume. 25 | The first is a matrix, which is represented with small circles. 26 | The size and shape of matrix grain particles are not critical, since 27 | the boundaries between them will be removed before triangular meshing. 28 | The second material consists of circular inclusions with diameter 2. 29 | 30 | Domain Geometry 31 | =============== 32 | 33 | The domain of the microstructure is a square with its bottom-left 34 | corner fixed to the origin. 35 | The side length is 20, which is 10x the size of the inclusions to ensure that 36 | the microstructure is statistically representative. 37 | 38 | 39 | Settings 40 | ======== 41 | 42 | Many settings have been left to their defaults, with the exceptions being the 43 | verbose mode and output directory. 44 | 45 | By default, MicroStructPy does not print status updates to the command line. 46 | Switching the verbose mode on will regularly print the status of the code. 47 | 48 | The output directory is a filepath for writing text and image files. 49 | By default, MicroStructPy outputs texts files containing data on the seeds, 50 | polygon mesh, and triangular mesh as well as the corresponding image files, 51 | saved in PNG format. 52 | 53 | .. note:: 54 | 55 | The ```` field can be an absolute or relative filepath. If it is 56 | relative, outputs are written relative to the **input file**, not the 57 | current working directory. 58 | 59 | 60 | Output Files 61 | ============ 62 | 63 | The three plots that this file generates are the seeding, the polygon mesh, 64 | and the triangular mesh. 65 | These three plots are shown in :numref:`f_ex_1_basic_seeds` - 66 | :numref:`f_ex_1_basic_tri`. 67 | 68 | .. _f_ex_1_basic_seeds: 69 | .. figure:: ../../../../src/microstructpy/examples/intro_1_basic/seeds.png 70 | :alt: Seed geometries. 71 | 72 | Introduction 1 - seed geometries. 73 | 74 | .. _f_ex_1_basic_poly: 75 | .. figure:: ../../../../src/microstructpy/examples/intro_1_basic/polymesh.png 76 | :alt: Polygonal mesh. 77 | 78 | Introduction 1 - polygonal mesh. 79 | 80 | .. _f_ex_1_basic_tri: 81 | .. figure:: ../../../../src/microstructpy/examples/intro_1_basic/trimesh.png 82 | :alt: Triangular mesh. 83 | 84 | Introduction 1 - triangular mesh. -------------------------------------------------------------------------------- /docs/source/examples/intro/intro_2.rst: -------------------------------------------------------------------------------- 1 | .. _ex_2_quality: 2 | 3 | ================ 4 | Quality Controls 5 | ================ 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``intro_2_quality.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=intro_2_quality.xml 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/intro_2_quality.xml 18 | :language: xml 19 | 20 | 21 | Materials 22 | ========= 23 | 24 | There are two materials, in a 2:1 ratio based on volume. 25 | The first is a matrix, which is represented with small circles. 26 | The second material consists of circular inclusions with diameter 2. 27 | 28 | Domain Geometry 29 | =============== 30 | 31 | These two materials fill a square domain. 32 | The bottom-left corner of the rectangle is the origin, which puts the 33 | rectangle in the first quadrant. 34 | The side length is 20, which is 10x the size of the inclusions to ensure that 35 | microstructure is statistically representative. 36 | 37 | 38 | Settings 39 | ======== 40 | 41 | The first two settings determine the output directory and whether to run the 42 | program in verbose mode. 43 | The following settings determine the quality of the triangular mesh. 44 | 45 | The minimum interior angle of the elements is 25 degrees, ensuring lower 46 | aspect ratios compared to the first example. 47 | The maximum area of the elements is also limited to 1, which populates the 48 | matrix with smaller elements. 49 | Finally, The maximum edge length of elements at interfaces is set to 0.1, 50 | which increasing the mesh density surrounding the inclusions and at the 51 | boundary of the domain. 52 | 53 | Note that the edge length control is currently unavailable in 3D. 54 | 55 | 56 | Output Files 57 | ============ 58 | 59 | The three plots that this file generates are the seeding, the polygon mesh, 60 | and the triangular mesh. 61 | These three plots are shown in :numref:`f_ex_2_quality_seeds` - 62 | :numref:`f_ex_2_quality_tri`. 63 | 64 | .. _f_ex_2_quality_seeds: 65 | .. figure:: ../../../../src/microstructpy/examples/intro_2_quality/seeds.png 66 | :alt: Seed geometries. 67 | 68 | Introduction 2 - seed geometries. 69 | 70 | .. _f_ex_2_quality_poly: 71 | .. figure:: ../../../../src/microstructpy/examples/intro_2_quality/polymesh.png 72 | :alt: Polygonal mesh. 73 | 74 | Introduction 2 - polygonal mesh. 75 | 76 | .. _f_ex_2_quality_tri: 77 | .. figure:: ../../../../src/microstructpy/examples/intro_2_quality/trimesh.png 78 | :alt: Triangular mesh. 79 | 80 | Introduction 2 - triangular mesh. -------------------------------------------------------------------------------- /docs/source/examples/intro/intro_3.rst: -------------------------------------------------------------------------------- 1 | .. _ex_3_shape: 2 | 3 | ============ 4 | Size & Shape 5 | ============ 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``intro_3_size_shape.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=intro_3_size_shape.xml 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/intro_3_size_shape.xml 18 | :language: xml 19 | 20 | 21 | Materials 22 | ========= 23 | 24 | There are two materials, in a 2:1 ratio based on volume. 25 | The first is a matrix, which is represented with small circles. 26 | 27 | The second material consists of elliptical inclusions with size ranging from 28 | 0 to 2 and aspect ratio ranging from 1 to 3. 29 | Note that the size is defined as the diameter of a circle with equivalent area. 30 | The orientation angle of the inclusions are random, specifically they are 31 | uniformly distributed from 0 to 360 degrees. 32 | 33 | Domain Geometry 34 | =============== 35 | 36 | These two materials fill a square domain. 37 | The bottom-left corner of the rectangle is the origin, which puts the 38 | rectangle in the first quadrant. 39 | The side length is 20, which is 10x the size of the inclusions 40 | to ensure that the microstructure is statistically representative. 41 | 42 | 43 | Settings 44 | ======== 45 | 46 | Many settings have been left to their defaults, with the exceptions being the 47 | verbose mode and output directory. 48 | 49 | By default, MicroStructPy does not print status updates to the command line. 50 | Switching the verbose mode on will regularly print the status of the code. 51 | 52 | The output directory is a filepath for writing text and image files. 53 | By default, MicroStructPy outputs texts files containing data on the seeds, 54 | polygon mesh, and triangular mesh as well as the corresponding image files, 55 | saved in PNG format. 56 | 57 | .. note:: 58 | 59 | The ```` field can be an absolute or relative filepath. If it is 60 | relative, outputs are written relative to the **input file**, not the 61 | current working directory. 62 | 63 | 64 | Output Files 65 | ============ 66 | 67 | The three plots that this file generates are the seeding, the polygon mesh, 68 | and the triangular mesh. 69 | These three plots are shown in :numref:`f_ex_3_size_shape_seeds` - 70 | :numref:`f_ex_3_size_shape_tri`. 71 | 72 | .. _f_ex_3_size_shape_seeds: 73 | .. figure:: ../../../../src/microstructpy/examples/intro_3_size_shape/seeds.png 74 | :alt: Seed geometries. 75 | 76 | Introduction 3 - seed geometries. 77 | 78 | .. _f_ex_3_size_shape_poly: 79 | .. figure:: ../../../../src/microstructpy/examples/intro_3_size_shape/polymesh.png 80 | :alt: Polygonal mesh. 81 | 82 | Introduction 3 - polygonal mesh. 83 | 84 | .. _f_ex_3_size_shape_tri: 85 | .. figure:: ../../../../src/microstructpy/examples/intro_3_size_shape/trimesh.png 86 | :alt: Triangular mesh. 87 | 88 | Introduction 3 - triangular mesh. -------------------------------------------------------------------------------- /docs/source/examples/intro/intro_4.rst: -------------------------------------------------------------------------------- 1 | .. _ex_4_oriented: 2 | 3 | =============== 4 | Oriented Grains 5 | =============== 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``intro_4_oriented.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=intro_4_oriented.xml 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/intro_4_oriented.xml 18 | :language: xml 19 | 20 | 21 | Materials 22 | ========= 23 | 24 | There are two materials, in a 2:1 ratio based on volume. 25 | The first is a matrix, which is represented with small circles. 26 | 27 | The second material consists of elliptical inclusions with size ranging from 28 | 0 to 2 and aspect ratio ranging from 1 to 3. 29 | Note that the size is defined as the diameter of a circle with equivalent area. 30 | The orientation angle of the inclusions are uniformly distributed between -10 31 | and +10 degrees, relative to the +x axis. 32 | 33 | Domain Geometry 34 | =============== 35 | 36 | These two materials fill a square domain. 37 | The bottom-left corner of the rectangle is the origin, which puts the 38 | rectangle in the first quadrant. 39 | The side length is 20, which is 10x the size of the inclusions 40 | to ensure that the microstructure is statistically representative. 41 | 42 | 43 | Settings 44 | ======== 45 | 46 | Many settings have been left to their defaults, with the exceptions being the 47 | verbose mode and output directory. 48 | 49 | By default, MicroStructPy does not print status updates to the command line. 50 | Switching the verbose mode on will regularly print the status of the code. 51 | 52 | The output directory is a filepath for writing text and image files. 53 | By default, MicroStructPy outputs texts files containing data on the seeds, 54 | polygon mesh, and triangular mesh as well as the corresponding image files, 55 | saved in PNG format. 56 | 57 | .. note:: 58 | 59 | The ```` field can be an absolute or relative filepath. If it is 60 | relative, outputs are written relative to the **input file**, not the 61 | current working directory. 62 | 63 | 64 | Output Files 65 | ============ 66 | 67 | The three plots that this file generates are the seeding, the polygon mesh, 68 | and the triangular mesh. 69 | These three plots are shown in :numref:`f_ex_4_oriented_seeds` - 70 | :numref:`f_ex_4_oriented_tri`. 71 | 72 | .. _f_ex_4_oriented_seeds: 73 | .. figure:: ../../../../src/microstructpy/examples/intro_4_oriented/seeds.png 74 | :alt: Seed geometries. 75 | 76 | Introduction 4 - seed geometries. 77 | 78 | .. _f_ex_4_oriented_poly: 79 | .. figure:: ../../../../src/microstructpy/examples/intro_4_oriented/polymesh.png 80 | :alt: Polygonal mesh. 81 | 82 | Introduction 4 - polygonal mesh. 83 | 84 | .. _f_ex_4_oriented_tri: 85 | .. figure:: ../../../../src/microstructpy/examples/intro_4_oriented/trimesh.png 86 | :alt: Triangular mesh. 87 | 88 | Introduction 4 - triangular mesh. 89 | -------------------------------------------------------------------------------- /docs/source/examples/intro/intro_5.rst: -------------------------------------------------------------------------------- 1 | .. _ex_5_plotting: 2 | 3 | ============= 4 | Plot Controls 5 | ============= 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``intro_5_plotting.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=intro_5_plotting.xml 14 | 15 | The full text of the file is given below. 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/intro_5_plotting.xml 18 | :language: xml 19 | 20 | 21 | Materials 22 | ========= 23 | 24 | There are two materials, in a 2:1 ratio based on volume. 25 | The first is a pink matrix, which is represented with small circles. 26 | 27 | The second material consists of lime green circular inclusions with diameter 2. 28 | 29 | Domain Geometry 30 | =============== 31 | 32 | These two materials fill a square domain. 33 | The bottom-left corner of the rectangle is the origin, which puts the 34 | rectangle in the first quadrant. 35 | The side length is 20, which is 10x the size of the inclusions. 36 | 37 | 38 | Settings 39 | ======== 40 | 41 | PNG files of each step in the process will be output, as well as the 42 | intermediate text files. 43 | They are saved in a folder named ``intro_5_plotting``, in the current directory 44 | (i.e ``./intro_5_plotting``). 45 | PDF files of the poly and tri mesh are also generated, plus an EPS file for the 46 | tri mesh. 47 | 48 | The seeds are plotted with transparency to show the overlap between them. 49 | The poly mesh is plotted with thick purple edges and the tri mesh is plotted 50 | with thin navy edges. 51 | 52 | In all of the plots, the axes are toggles off, creating image files with 53 | minimal borders. 54 | 55 | 56 | Output Files 57 | ============ 58 | 59 | The three plots that this file generates are the seeding, the polygon mesh, 60 | and the triangular mesh. 61 | These three plots are shown in :numref:`f_ex_5_plotting_seeds` - 62 | :numref:`f_ex_5_plotting_tri`. 63 | 64 | .. _f_ex_5_plotting_seeds: 65 | .. figure:: ../../../../src/microstructpy/examples/intro_5_plotting/seeds.png 66 | :alt: Seed geometries. 67 | 68 | Introduction 5 - seed geometries. 69 | 70 | .. _f_ex_5_plotting_poly: 71 | .. figure:: ../../../../src/microstructpy/examples/intro_5_plotting/polymesh.png 72 | :alt: Polygonal mesh. 73 | 74 | Introduction 5 - polygonal mesh. 75 | 76 | .. _f_ex_5_plotting_tri: 77 | .. figure:: ../../../../src/microstructpy/examples/intro_5_plotting/trimesh.png 78 | :alt: Triangular mesh. 79 | 80 | Introduction 5 - triangular mesh. -------------------------------------------------------------------------------- /docs/source/examples/intro/intro_6.rst: -------------------------------------------------------------------------------- 1 | .. _ex_6_culmin: 2 | 3 | =========== 4 | Culmination 5 | =========== 6 | 7 | XML Input File 8 | ============== 9 | 10 | The basename for this file is ``intro_6_culmination.xml``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=intro_6_culmination.xml 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/intro_6_culmination.xml 18 | :language: xml 19 | 20 | 21 | Materials 22 | ========= 23 | 24 | There are two materials, in a 2:1 ratio based on volume. 25 | The first is a pink matrix, which is represented with small circles. 26 | 27 | The second material consists of lime green elliptical inclusions with size 28 | ranging from 0 to 2 and aspect ratio ranging from 1 to 3. 29 | Note that the size is defined as the diameter of a circle with equivalent area. 30 | The orientation angle of the inclusions are uniformly distributed between -10 31 | and +10 degrees, relative to the +x axis. 32 | 33 | Domain Geometry 34 | =============== 35 | 36 | These two materials fill a square domain. 37 | The bottom-left corner of the rectangle is the origin, which puts the 38 | rectangle in the first quadrant. 39 | The side length is 20, which is 10x the size of the inclusions. 40 | 41 | 42 | Settings 43 | ======== 44 | 45 | PNG files of each step in the process will be output, as well as the 46 | intermediate text files. 47 | They are saved in a folder named ``intro_5_plotting``, in the current directory 48 | (i.e ``./intro_5_plotting``). 49 | PDF files of the poly and tri mesh are also generated, plus an EPS file for the 50 | tri mesh. 51 | 52 | The seeds are plotted with transparency to show the overlap between them. 53 | The poly mesh is plotted with thick purple edges and the tri mesh is plotted 54 | with thin navy edges. 55 | 56 | In all of the plots, the axes are toggles off, creating image files with 57 | minimal borders. 58 | 59 | The minimum interior angle of the elements is 25 degrees, ensuring lower 60 | aspect ratios compared to the first example. 61 | The maximum area of the elements is also limited to 1, which populates the 62 | matrix with smaller elements. 63 | Finally, The maximum edge length of elements at interfaces is set to 0.1, 64 | which increasing the mesh density surrounding the inclusions and at the 65 | boundary of the domain. 66 | 67 | Note that the edge length control is currently unavailable in 3D. 68 | 69 | 70 | Output Files 71 | ============ 72 | 73 | The three plots that this file generates are the seeding, the polygon mesh, 74 | and the triangular mesh. 75 | These three plots are shown in :numref:`f_ex_6_culmination_seeds` - 76 | :numref:`f_ex_6_culmination_tri`. 77 | 78 | .. _f_ex_6_culmination_seeds: 79 | .. figure:: ../../../../src/microstructpy/examples/intro_6_culmination/seeds.png 80 | :alt: Seed geometries. 81 | 82 | Introduction 6 - seed geometries. 83 | 84 | .. _f_ex_6_culmination_poly: 85 | .. figure:: ../../../../src/microstructpy/examples/intro_6_culmination/polymesh.png 86 | :alt: Polygonal mesh. 87 | 88 | Introduction 6 - polygonal mesh. 89 | 90 | .. _f_ex_6_culmination_tri: 91 | .. figure:: ../../../../src/microstructpy/examples/intro_6_culmination/trimesh.png 92 | :alt: Triangular mesh. 93 | 94 | Introduction 6 - triangular mesh. -------------------------------------------------------------------------------- /docs/source/examples/package/foam.rst: -------------------------------------------------------------------------------- 1 | .. _ex_foam: 2 | 3 | ==== 4 | Foam 5 | ==== 6 | 7 | In this example, a foam microstructure is generated by first tesselating the 8 | voids, then adding foam material to the edges between the voids. 9 | 10 | Python Script 11 | ============= 12 | 13 | The basename for this file is ``foam.py``. 14 | The file can be run using this command:: 15 | 16 | microstructpy --demo=foam.py 17 | 18 | The full text of the script is: 19 | 20 | .. literalinclude:: ../../../../src/microstructpy/examples/foam.py 21 | :language: python 22 | 23 | Domain 24 | ====== 25 | 26 | The domain of the microstructure is a :class:`.Square`. 27 | Without arguments, the square's center is (0, 0) and side length is 15. 28 | 29 | Seeds 30 | ===== 31 | 32 | Initially, the seed list is entirely voids following a lognormal size 33 | distribution. 34 | These are then tessellated to determine the boundaries between the voids. 35 | These voids are generated to fill 70% of the domain and are positioned with 36 | a custom ``rtol`` value of 3%. 37 | This ensures that most of the voids do not connect with each other and that 38 | the foam seeds positioned along the edges do not become consumed by the void 39 | cells. 40 | 41 | Next, foam seeds are added to the edges between voids. 42 | These seeds are given a size distribution, however there are no foam grains 43 | since the material is amorphous. 44 | Once the foam seeds are positioned in the domain, the lists of void and foam 45 | seeds are combined into a single seed list. 46 | 47 | Polygon Mesh 48 | ============ 49 | 50 | A polygon mesh is created from the list of seed points using the 51 | :func:`~microstructpy.meshing.PolyMesh.from_seeds` class method. 52 | 53 | Triangular Mesh 54 | =============== 55 | 56 | A triangular mesh is created from the polygonal mesh using the 57 | :func:`~microstructpy.meshing.TriMesh.from_polymesh` class method. 58 | The optional ``phases`` parameter is used in this case since the mesh contains 59 | non-crystalline materials. 60 | Additionally, the minimum interior angle of the mesh elements is set to 20 to 61 | ensure good mesh quality and the maximum edge length is set to increase mesh 62 | resolution near the voids. 63 | 64 | Plotting 65 | ======== 66 | 67 | The triangular mesh in this example is plotted in aquamarine, one of 68 | several named colors in matplotlib. 69 | Next, the axes are turned off and the limits are set to equal the bounds of 70 | the domain. 71 | Finally, the triangular mesh is saved as a PNG and as a PDF, with the 72 | resulting plot shown in :numref:`f_ex_foam_tri`. 73 | 74 | .. _f_ex_foam_tri: 75 | .. figure:: ../../../../src/microstructpy/examples/foam/trimesh.png 76 | 77 | Foam example - triangular mesh. 78 | -------------------------------------------------------------------------------- /docs/source/examples/package/from_image.rst: -------------------------------------------------------------------------------- 1 | .. _ex_from_image: 2 | 3 | ========================= 4 | Microstructure from Image 5 | ========================= 6 | 7 | .. note:: 8 | 9 | The open source and freely available software package OOF is better equiped 10 | to create unstructured meshes from images. 11 | 12 | https://www.ctcms.nist.gov/oof/ 13 | 14 | 15 | Python Script 16 | ============= 17 | 18 | The basename for this file is ``from_image.py``. 19 | The file can be run using this command:: 20 | 21 | microstructpy --demo=from_image.py 22 | 23 | The full text of the script is: 24 | 25 | .. literalinclude:: ../../../../src/microstructpy/examples/from_image.py 26 | :language: python 27 | 28 | Read Image 29 | ========== 30 | 31 | The first section of the script reads the image using matplotlib. 32 | The brightness of the image is taken as the red channel, since the RGB values 33 | are equal. That image is shown in :numref:`f_ex_image_in`. 34 | 35 | .. _f_ex_image_in: 36 | .. figure:: ../../../../src/microstructpy/examples/aluminum_micro.png 37 | :alt: Micrograph of aluminum. 38 | 39 | Micrograph of aluminum. 40 | 41 | Bin Pixels 42 | ========== 43 | 44 | The pixel values are binned based on whether the brightness is above or 45 | below 0.5. 46 | 47 | Phases 48 | ====== 49 | 50 | The two phases are considered amorphous, to prevent pixilation in the 51 | triangle mesh. 52 | 53 | Create the Polygon Mesh 54 | ======================= 55 | 56 | The polygon mesh is a reproduction of the pixel grid in the image. 57 | The facets are edges between pixels, and the polygons are all squares. 58 | 59 | Create the Triangle Mesh 60 | ======================== 61 | 62 | The triangle mesh is created from the polygon mesh and uses the amorphous 63 | specifications for the phases. The minimum interior angle of the triangles 64 | is set to 20 degrees to control the aspect ratio of triangles. 65 | 66 | Plot Triangle Mesh 67 | ================== 68 | 69 | The axes of the plot are switched off, then the triangle mesh is plotted. 70 | The color of each triangle is set by the phase. 71 | 72 | Save Plot and Copy Input File 73 | ============================= 74 | 75 | The final plot is saved to a file, then the input image is copied to the 76 | same directory for comparison. 77 | The output PNG file of this script is shown in :numref:`f_ex_image_out`. 78 | 79 | .. _f_ex_image_out: 80 | .. figure:: ../../../../src/microstructpy/examples/from_image/trimesh.png 81 | :alt: Triangular mesh of aluminum microstructure. 82 | 83 | Triangular mesh of aluminum microstructure. 84 | 85 | 86 | -------------------------------------------------------------------------------- /docs/source/examples/package/grain_neighborhoods.rst: -------------------------------------------------------------------------------- 1 | .. _ex_grain_nbr: 2 | 3 | =================== 4 | Grain Neighborhoods 5 | =================== 6 | 7 | Python Script 8 | ============= 9 | 10 | The basename for this file is ``grain_neighborhoods.py``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=grain_neighborhoods.py 14 | 15 | The full text of the script is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/grain_neighborhoods.py 18 | :language: python 19 | 20 | Domain 21 | ====== 22 | 23 | The domain of the microstructure is a 24 | :class:`microstructpy.geometry.Rectangle`, with the bottom left corner at the 25 | origin and side lengths of 8 and 12. 26 | 27 | Phases 28 | ====== 29 | 30 | There are initially two phases: a matrix phase and a neighborhood phase. 31 | The neighborhood phase will be broken down into materials later. The matrix 32 | phase occupies two thirds of the domain, while the neighborhoods occupy one 33 | third. 34 | 35 | Seeds 36 | ===== 37 | 38 | The seeds are generated to fill 1.1x the area of the domain, to account for 39 | overlap with the boundaries. They are positioned according to random uniform 40 | distributions. 41 | 42 | Neighborhood Replacement 43 | ======================== 44 | 45 | The neighborhood seeds are replaced by a set of three different materials. 46 | One material occupies the center of the neighborhood, while the other two 47 | alternate in a ring around the center. 48 | 49 | Polygon and Triangle Meshing 50 | ============================ 51 | 52 | The seeds are converted into a triangular mesh by first constructing a 53 | polygon mesh. Each material is solid, except for the first which is designated 54 | as a matrix phase. Mesh quality controls are specified to prevent high aspect 55 | ratio triangles. 56 | 57 | Plotting 58 | ======== 59 | 60 | The triangular mesh is plotted and saved to a file. 61 | Each triangle is colored based on its material phase, using the standard 62 | matplotlib colors: C0, C1, C2, etc. 63 | The output PNG file is shown in :numref:`f_ex_neighs_tri`. 64 | 65 | .. _f_ex_neighs_tri: 66 | .. figure:: ../../../../src/microstructpy/examples/grain_neighborhoods/trimesh.png 67 | :alt: Triangular mesh of microstructure with seed neighborhoods. 68 | 69 | Triangular mesh of microstructure with seed neighborhoods. -------------------------------------------------------------------------------- /docs/source/examples/package/logo.rst: -------------------------------------------------------------------------------- 1 | .. _ex_logo: 2 | 3 | ================== 4 | MicroStructPy Logo 5 | ================== 6 | 7 | Python Script 8 | ============= 9 | 10 | The basename for this file is ``logo.py``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=logo.py 14 | 15 | The full text of the script is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/logo.py 18 | :language: python 19 | 20 | Domain 21 | ====== 22 | 23 | The domain of the microstructure is a :class:`.Circle`. 24 | Without arguments, the circle's center is (0, 0) and side length is 1. 25 | 26 | Seeds 27 | ===== 28 | 29 | The seeds are 14 circles with radii uniformly distributed from 0 to 0.3. 30 | Calling the :func:`~microstructpy.seeding.SeedList.position` method 31 | positions the points according to random uniform distributions in the domain. 32 | 33 | Polygon Mesh 34 | ============ 35 | 36 | A polygon mesh is created from the list of seed points using the 37 | :func:`~microstructpy.meshing.PolyMesh.from_seeds` class method. 38 | The mesh is plotted and saved into a PNG file in the remaining lines of the 39 | script. 40 | 41 | Plot Logo 42 | ========= 43 | 44 | The edges in the polygonal mesh are plotted white on a black background. 45 | This image is converted into a mask and the white pixels are converted into 46 | transparent pixels. 47 | The remaining pixels are colored with a linear gradient between two colors. 48 | This image is saved in padded and tight versions, as well as a favicon for the 49 | HTML documentation. 50 | The logo that results is shown in :numref:`f_ex_logo`. 51 | 52 | .. _f_ex_logo: 53 | .. figure:: ../../../../src/microstructpy/examples/logo/logo.png 54 | :alt: MicroStructPy logo. 55 | 56 | MicroStructPy logo. 57 | 58 | -------------------------------------------------------------------------------- /docs/source/examples/package/mesh_process.rst: -------------------------------------------------------------------------------- 1 | .. _ex_docs_banner: 2 | 3 | =========================== 4 | Microstructure Mesh Process 5 | =========================== 6 | 7 | Python Script 8 | ============= 9 | 10 | The basename for this file is ``docs_banner.py``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=docs_banner.py 14 | 15 | The full text of the file is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/docs_banner.py 18 | :language: python 19 | 20 | Domain Geometry 21 | =============== 22 | 23 | The materials fill a rectangular domain with side lengths 20 and 10. 24 | The center of the rectangle defaults to the origin. 25 | 26 | Seeds 27 | ===== 28 | 29 | The first material is phase 2, which contains a single elliptical seed with 30 | semi-axes 8 and 3. 31 | Next, phases 0 and 1 are created with identical size distributions and 32 | different colors. 33 | The size distributions are uniform random from 0.5 to 1.5. 34 | Seeds of phase 0 and phase 1 are generated to fill the area between the 35 | rectangular domain and the elliptical seed from phase 2. 36 | 37 | Next, the phase 2 seed is appended to the list of phase 0 and 1 seeds. 38 | A hold list is then created to indicate to 39 | :func:`~microstructpy.seeding.SeedList.position` 40 | which seeds should have their positions (centers) held. 41 | The default position of a seed is the origin, so by setting the hold flag to 42 | ``True`` for the elliptical seed, it will be fixed to the center of the domain 43 | while the remaining seeds will be randomly positioned around it. 44 | 45 | Polygonal and Triangular Meshing 46 | ================================ 47 | 48 | Once the seeds are positioned in the domain, a polygonal mesh is created using 49 | :func:`~microstructpy.meshing.PolyMesh.from_seeds`. 50 | The triangular mesh is created using 51 | :func:`~microstructpy.meshing.TriMesh.from_polymesh`, 52 | with the quality control settings ``min_angle``, ``max_edge_length``, and 53 | ``max_volume``. 54 | 55 | Plot Figure 56 | =========== 57 | 58 | The figure contains three plots: the seeds, the polygonal mesh, and the 59 | triangular/unstructured mesh. 60 | First, the seeds plot is generated using SeedList 61 | :func:`~microstructpy.seeding.SeedList.plot` 62 | and Rectangle 63 | :func:`~microstructpy.geometry.Rectangle.plot` 64 | to show the boundary of the domain. 65 | The seeds are plotted with some transparency to show overlap. 66 | 67 | Next, the polygonal mesh is translated to the right and plotted in such a way 68 | that avoids the internal geometry of the elliptical seed. 69 | This internal geometry is created by the multi-circle approximation used in 70 | polygonal meshing, then removed during the triangular meshing process. 71 | In the interest of clarity, these two steps are combined and the elliptical 72 | grain is plotted without internal geomtry. 73 | 74 | Finally, the triangular mesh is translated to the right of the polygonal mesh 75 | and plotted using TriMesh 76 | :func:`~microstructpy.meshing.TriMesh.plot`. 77 | 78 | Once all three plots have been added to the figure, the axes and 79 | aspect ratio are adjusted. 80 | This figure is shown in :numref:`f_ex_process`. 81 | The PNG and PDF versions of this plot are saved in a folder named 82 | ``docs_banner``, in the current directory (i.e ``./docs_banner``). 83 | 84 | .. _f_ex_process: 85 | .. figure:: ../../../../src/microstructpy/examples/docs_banner/banner.png 86 | :alt: Microstructure meshing process. 87 | 88 | Microstructure meshing process. 89 | 90 | The three major steps are: 91 | 1) seed the domain with particles, 92 | 2) create a Voronoi power diagram, and 93 | 3) convert the diagram into an unstructured mesh. -------------------------------------------------------------------------------- /docs/source/examples/package/standard_voronoi.rst: -------------------------------------------------------------------------------- 1 | .. _ex_std_voro: 2 | 3 | ======================== 4 | Standard Voronoi Diagram 5 | ======================== 6 | 7 | Python Script 8 | ============= 9 | 10 | The basename for this file is ``standard_voronoi.py``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=standard_voronoi.py 14 | 15 | The full text of the script is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/standard_voronoi.py 18 | :language: python 19 | 20 | Domain 21 | ====== 22 | 23 | The domain of the microstructure is a :class:`.Square`. 24 | Without arguments, the square's center is (0, 0) and side length is 1. 25 | 26 | Seeds 27 | ===== 28 | 29 | A set of 50 seed circles with small radius is initially created. 30 | Calling the :func:`~microstructpy.seeding.SeedList.position` method 31 | positions the points according to random uniform distributions in the domain. 32 | 33 | Polygon Mesh 34 | ============ 35 | 36 | A polygon mesh is created from the list of seed points using the 37 | :func:`~microstructpy.meshing.PolyMesh.from_seeds` class method. 38 | The mesh is plotted and saved into a PNG file in the remaining lines of the 39 | script. 40 | 41 | Plotting 42 | ======== 43 | 44 | The output Voronoi diagram is plotted in :numref:`f_ex_voro`. 45 | 46 | .. _f_ex_voro: 47 | .. figure:: ../../../../src/microstructpy/examples/standard_voronoi/voronoi_diagram.png 48 | 49 | Standard Voronoi diagram. 50 | -------------------------------------------------------------------------------- /docs/source/examples/package/uniform_seeding.rst: -------------------------------------------------------------------------------- 1 | .. _ex_uni_seed: 2 | 3 | =============================== 4 | Uniform Seeding Voronoi Diagram 5 | =============================== 6 | 7 | Python Script 8 | ============= 9 | 10 | The basename for this file is ``uniform_seeding.py``. 11 | The file can be run using this command:: 12 | 13 | microstructpy --demo=uniform_seeding.py 14 | 15 | The full text of the script is: 16 | 17 | .. literalinclude:: ../../../../src/microstructpy/examples/uniform_seeding.py 18 | :language: python 19 | 20 | Domain 21 | ====== 22 | 23 | The domain of the microstructure is a :class:`.Square`. 24 | Without arguments, the square's center is (0, 0) and side length is 1. 25 | 26 | Seeds 27 | ===== 28 | 29 | A set of 200 seed circles with small radius is initially created. 30 | The positions of the seeds are set with Mitchell's Best Candidate Algorithm 31 | [#f1]_. This algorithm positions seed *i* by sampling *i + 1* 32 | random points and picking the one that is furthest from its nearest neighbor. 33 | 34 | Polygon Mesh 35 | ============ 36 | 37 | A polygon mesh is created from the list of seed points using the 38 | :func:`~microstructpy.meshing.PolyMesh.from_seeds` class method. 39 | 40 | Plotting 41 | ======== 42 | 43 | The facecolor of each polygon is determined by its area. If it is below the 44 | standard area (domain area / number of cells), then it is shaded blue. If 45 | it is above the standard area, it is shaded red. A custom colorbar is added 46 | to the figure and it is saved as a PNG, shown in :numref:`f_ex_uni_voro`. 47 | 48 | .. _f_ex_uni_voro: 49 | .. figure:: ../../../../src/microstructpy/examples/uniform_seeding/voronoi_diagram.png 50 | :alt: Voronoi diagram with uniformly-spaced seeds, colored by area. 51 | 52 | Uniformly seeded Voronoi diagram with cells colored by area. 53 | 54 | 55 | .. [#f1] Mitchell, T.J., "An Algorithm for the Construction of "D-Optimal" 56 | Experimental Designs," Technometrics, Vol. 16, No. 2, May 1974, pp. 203-210. 57 | (https://www.jstor.org/stable/1267940) 58 | -------------------------------------------------------------------------------- /docs/source/getting_started.rst: -------------------------------------------------------------------------------- 1 | .. _getting_started: 2 | 3 | Getting Started 4 | =============== 5 | 6 | Download & Installation 7 | ----------------------- 8 | 9 | To install MicroStructPy, download it from PyPI using:: 10 | 11 | pip install microstructpy 12 | 13 | This installs both the ``microstructpy`` Python package and the 14 | ``microstructpy`` command line interface (CLI). 15 | If there is an error with the install, try to install ``pybind11`` first. 16 | You may need to add the ``--user`` flag, depending on your permissions. 17 | 18 | To verify installation of the package, run the following commands:: 19 | 20 | python -c 'import microstructpy' 21 | microstructpy --help 22 | 23 | This verifies that 1) the python package has installed correctly and 2) the 24 | command line interface (CLI) has been found by the shell. 25 | If there is an issue with the CLI, the install location may not be in the 26 | PATH variable. 27 | The most likely install location is ``~/.local/bin`` for Mac or Linux machines. 28 | For Windows, it may be in a path similar to 29 | ``~\AppData\Roaming\Python\Python36\Scripts\``. 30 | 31 | .. note:: 32 | If the install fails and the last several error messages reference 33 | ``pybind11``, run ``pip install pybind11`` first then install MicroStructPy. 34 | 35 | Running Demonstrations 36 | ---------------------- 37 | 38 | MicroStructPy comes with several demonstrations to familiarize users with its 39 | capabilities and options. 40 | A demonstration can be run from the command line by:: 41 | 42 | microstructpy --demo=minimal.xml 43 | 44 | When a demo is run, the XML input file is copied to the current working 45 | directory. 46 | See :ref:`examples_page` for a full list of available examples and 47 | demostrations. 48 | 49 | 50 | Development 51 | ----------- 52 | 53 | Contributions to MicroStructPy are most welcome. 54 | To download and install the source code for the project:: 55 | 56 | git clone https://github.com/kip-hart/MicroStructPy.git 57 | pip install -e MicroStructPy 58 | 59 | MicroStructPy uses tox_ to run tests and build the documentation. 60 | To perform these tests:: 61 | 62 | pip install tox 63 | cd MicroStructPy 64 | tox 65 | 66 | Please use the issue and pull request features of the `GitHub repository`_ 67 | to report bugs and modify the code. 68 | 69 | 70 | 71 | 72 | .. _`GitHub repository`: https://github.com/kip-hart/MicroStructPy 73 | .. _tox: https://tox.readthedocs.io 74 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. MicroStructPy documentation master file. 2 | 3 | .. include:: ../../README.rst 4 | :end-before: end-badges 5 | 6 | .. include:: welcome.rst 7 | :start-after: index-start 8 | 9 | .. toctree:: 10 | :hidden: 11 | :maxdepth: 2 12 | 13 | getting_started 14 | examples/index 15 | cli/index 16 | package_guide 17 | file_formats 18 | api/index 19 | troubleshooting 20 | changelog 21 | 22 | 23 | .. external images and shields 24 | 25 | .. include:: ../../README.rst 26 | :start-after: external-images 27 | -------------------------------------------------------------------------------- /docs/source/sphinx_gallery/README.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Sphinx Gallery 4 | ============== 5 | -------------------------------------------------------------------------------- /docs/source/sphinx_gallery/geometry/README.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Sphinx Gallery 4 | ============== 5 | -------------------------------------------------------------------------------- /docs/source/sphinx_gallery/geometry/plot_ellipse.py: -------------------------------------------------------------------------------- 1 | """ 2 | Plot figures relevant to :class:`microstructpy.geometry.Ellipse` 3 | 4 | """ 5 | 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | from matplotlib import collections 9 | from matplotlib.ticker import FormatStrFormatter 10 | 11 | import microstructpy as msp 12 | 13 | 14 | def main(): 15 | breakdown() # ellipse_001.png - ellipse breakdown 16 | 17 | 18 | def breakdown(): 19 | a = 3 20 | b = 1 21 | x1 = 0.7 22 | 23 | fig = plt.figure(figsize=(14, 6)) 24 | ellipse = msp.geometry.Ellipse(a=a, b=b) 25 | approx = ellipse.approximate(x1) 26 | ellipse.plot(edgecolor='k', facecolor='none', lw=3) 27 | t = np.linspace(0, 2 * np.pi) 28 | for x, y, r in approx: 29 | plt.plot(x + r * np.cos(t), y + r * np.sin(t), 'b') 30 | 31 | xticks = np.unique(np.concatenate((approx[:, 0], (-a, a)))) 32 | plt.xticks(xticks) 33 | plt.yticks(np.unique(np.concatenate((approx[:, 1], (-b, b))))) 34 | plt.gca().set_xticklabels([str(round(float(label), 1)) for label in xticks]) 35 | plt.axis('scaled') 36 | plt.grid(True, linestyle=':') 37 | plt.tight_layout() 38 | 39 | 40 | if __name__ == '__main__': 41 | plt.rc('savefig', dpi=300, pad_inches=0) 42 | main() -------------------------------------------------------------------------------- /docs/source/sphinx_gallery/geometry/plot_rectangle.py: -------------------------------------------------------------------------------- 1 | """ 2 | Plot figures relevant to :class:`microstructpy.geometry.Rectangle` 3 | 4 | """ 5 | 6 | import matplotlib.pyplot as plt 7 | import numpy as np 8 | from matplotlib import collections 9 | 10 | import microstructpy as msp 11 | 12 | def main(): 13 | # Plot breakdown 14 | breakdown(2.5, 1, 0.3, (9, 4)) # rectangle_001.png - rect breakdown 15 | breakdown(1, 1, 0.2, (4, 4)) # rectangle_001.png - square breakdown 16 | 17 | 18 | def breakdown(length, width, x1, figsize): 19 | r = msp.geometry.Rectangle(length=length, width=width) 20 | approx = r.approximate(x1=x1) 21 | 22 | # Plot rectangle 23 | fig = plt.figure(figsize=figsize) 24 | r.plot(edgecolor='k', facecolor='none', lw=3) 25 | 26 | # Plot breakdown 27 | t = np.linspace(0, 2 * np.pi) 28 | xp = np.cos(t) 29 | yp = np.sin(t) 30 | for x, y, radius in approx: 31 | plt.plot(x + radius * xp, y + radius * yp, 'b') 32 | 33 | # Format Axes 34 | xtick = np.unique([circ[0] for circ in approx]) 35 | ytick = np.unique([circ[1] for circ in approx]) 36 | plt.xticks(xtick) 37 | plt.yticks(ytick) 38 | 39 | plt.axis('scaled') 40 | plt.grid(True, linestyle=':') 41 | plt.tight_layout() 42 | 43 | 44 | if __name__ == '__main__': 45 | plt.rc('savefig', dpi=300, pad_inches=0) 46 | main() -------------------------------------------------------------------------------- /docs/source/sphinx_gallery/plot_demos.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r""" 3 | Plot all demos 4 | 5 | """ 6 | 7 | import glob 8 | import locale 9 | import os 10 | import shutil 11 | import subprocess 12 | 13 | import matplotlib.pyplot as plt 14 | import microstructpy as msp 15 | 16 | locale.setlocale(locale.LC_NUMERIC, "C") 17 | 18 | example_dir = '../../../src/microstructpy/examples' 19 | 20 | welcome_fnames = ['intro_2_quality/trimesh.png', 21 | 'minimal/polymesh.png', 22 | 'basalt_circle/trimesh.png', 23 | 'foam/trimesh.png', 24 | 'two_phase_3D/trimesh.png', 25 | 'colormap/trimesh.png'] 26 | 27 | def main(): 28 | # Copy Supporting Files 29 | supporting_files = ['aphanitic_cdf.csv', 'olivine_cdf.csv'] 30 | for fname in supporting_files: 31 | filename = os.path.join(example_dir, fname) 32 | shutil.copy(filename, '.') 33 | 34 | # Run XML files 35 | xml_pattern = os.path.join(example_dir, '*.xml') 36 | for filename in glob.glob(xml_pattern): 37 | msp.cli.run_file(filename) 38 | 39 | # Run Python Scripts 40 | py_pattern = os.path.join(example_dir, '*.py') 41 | for filename in glob.glob(py_pattern): 42 | subprocess.call(['python', filename]) 43 | 44 | # Remove Supporting Files 45 | for fname in supporting_files: 46 | os.remove(fname) 47 | 48 | # Create welcome figure 49 | create_welcome() 50 | 51 | # Create example subfigures 52 | seed_poly_tri('.') 53 | 54 | 55 | def create_welcome(): 56 | fig, axes = plt.subplots(2, 3, figsize=(21, 15)) 57 | plt.subplots_adjust(wspace=0.05, hspace=0) 58 | for i, fname in enumerate(welcome_fnames): 59 | filename = os.path.join(example_dir, fname) 60 | im = plt.imread(filename) 61 | 62 | row_num = int(i / 3) 63 | col_num = i % 3 64 | ax = axes[row_num, col_num] 65 | ax.imshow(im) 66 | 67 | ax.set_axis_off() 68 | ax.get_xaxis().set_visible(False) 69 | ax.get_yaxis().set_visible(False) 70 | 71 | sub_fname = 'welcome_examples.png' 72 | sub_filename = os.path.join(example_dir, sub_fname) 73 | plt.tight_layout() 74 | plt.savefig(sub_filename, pad_inches=0, dpi=200) 75 | plt.close('all') 76 | 77 | 78 | def seed_poly_tri(filepath): 79 | basenames = ['seeds.png', 'polymesh.png', 'trimesh.png'] 80 | ex_path = os.path.join(example_dir, filepath) 81 | fig, axes = plt.subplots(1, 3, figsize=(20, 10)) 82 | plt.subplots_adjust(wspace=0.05, hspace=0) 83 | for i, fname in enumerate(basenames): 84 | filename = os.path.join(ex_path, fname) 85 | im = plt.imread(filename) 86 | 87 | ax = axes[i] 88 | ax.imshow(im) 89 | 90 | ax.set_axis_off() 91 | ax.get_xaxis().set_visible(False) 92 | ax.get_yaxis().set_visible(False) 93 | 94 | sub_fname = 'joined.png' 95 | sub_filename = os.path.join(ex_path, sub_fname) 96 | plt.tight_layout() 97 | plt.savefig(sub_filename, pad_inches=0, dpi=300) 98 | plt.close('all') 99 | 100 | 101 | if __name__ == '__main__': 102 | main() 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /docs/source/troubleshooting.rst: -------------------------------------------------------------------------------- 1 | .. _troubleshooting: 2 | 3 | Troubleshooting 4 | ============================================================================== 5 | 6 | This page addresses some problems that may be encountered with MicroStructPy. 7 | If this page does not address your problem, please submit an issue through the 8 | package GitHub_ page. 9 | 10 | .. _GitHub: https://github.com/kip-hart/MicroStructPy 11 | 12 | Installation 13 | ------------------------------------------------------------------------------ 14 | 15 | These are problems encountered when installing MicroStructPy. 16 | 17 | Missing library for pygmsh on Linux 18 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 19 | **Problem Description** 20 | 21 | When running MicroStructPy for the first time on a Linux operating system, 22 | there is an error message like:: 23 | 24 | ... 25 | src/microstructpy/meshing/trimesh.py:19: in 26 | import pygmsh as pg 27 | /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages/pygmsh/__init__.py:1: in 28 | from . import geo, occ 29 | /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages/pygmsh/geo/__init__.py:1: in 30 | from .geometry import Geometry 31 | /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages/pygmsh/geo/geometry.py:1: in 32 | import gmsh 33 | /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages/gmsh-4.6.0-Linux64-sdk/lib/gmsh.py:39: in 34 | lib = CDLL(libpath) 35 | /opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/ctypes/__init__.py:364: in __init__ 36 | self._handle = _dlopen(self._name, mode) 37 | E OSError: libGLU.so.1: cannot open shared object file: No such file or directory 38 | 39 | 40 | **Problem Solution** 41 | 42 | The libGLU library is misssing from the computer. To add it, run:: 43 | 44 | sudo apt-get install libglu1 45 | 46 | 47 | MeshPy fails to install 48 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 49 | **Problem Description** 50 | 51 | When installing the package, either through PyPI as 52 | ``pip install microstructpy`` or from the source as ``pip install -e .`` in 53 | the top-level directory, an error message appears during the meshpy install. 54 | The error message indicates that Visual Studio cannot find the pybind11 55 | headers. 56 | 57 | **Problem Solution** 58 | 59 | Install pybind11 first by running ``pip install pybind11``, then try to 60 | install MicroStructPy. 61 | 62 | Command Line Interface 63 | ------------------------------------------------------------------------------ 64 | 65 | These are problems encountered when running ``microstructpy input_file.xml``. 66 | 67 | Command not found on Linux 68 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 69 | 70 | **Problem Description** 71 | 72 | The MicroStructPy package installs without a problem, however on running 73 | ``microstructpy example_file.xml`` the following message appears:: 74 | 75 | microstructpy: command not found 76 | 77 | **Problem Solution** 78 | 79 | The command line interface (CLI) is install to a directory that is not in 80 | the PATH variable. Check for the CLI in ``~/.local/bin`` and if it is there, 81 | add the following to your ``~/.bash_profile`` file:: 82 | 83 | export PATH=$PATH:~/.local/bin 84 | 85 | then source the .bash_profile file by running ``source ~/.bash_profile``. 86 | 87 | 'tkinter' not found on Linux 88 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 89 | 90 | **Problem Description** 91 | 92 | The MicroStructPy package installs without a problem, however on running 93 | ``microstructpy example_file.xml`` the following error is raised:: 94 | 95 | ModuleNotFoundError: No module named 'tkinter' 96 | 97 | **Problem Solution** 98 | 99 | To install ``tkinter`` for Python 3 on Linux, run the following command:: 100 | 101 | sudo apt-get install python3-tk 102 | 103 | For Python 2, run the following instead:: 104 | 105 | sudo apt-get install python-tk 106 | 107 | Program quits/segfaults while calculating Voronoi diagram 108 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 109 | 110 | **Problem Description** 111 | 112 | During the calculating Voronoi diagram step, the program either quits or 113 | segfaults. 114 | 115 | **Problem Solution** 116 | 117 | This issue was experienced while running 32-bit Python with a large number of 118 | seeds. Python ran out of memory addresses and segfaulted. Switching from 32-bit 119 | to 64-bit Python solved the problem. 120 | -------------------------------------------------------------------------------- /docs/source/welcome.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Welcome 4 | ======= 5 | 6 | Summary 7 | ------- 8 | 9 | .. index-start 10 | 11 | MicroStructPy is a microstructure mesh generator written in Python. 12 | Features of MicroStructPy include: 13 | 14 | * 2D and 3D microstructures 15 | * Grain size, shape, orientation, and position control 16 | * Polycrystals, amorphous phases, and voids 17 | * Mesh verification 18 | * Visualizations 19 | * Output to common file formats 20 | * Customizable workflow 21 | 22 | .. figure:: ../../src/microstructpy/examples/docs_banner/banner.png 23 | :alt: Banner image showing the three steps for creating microstructure. 24 | 25 | The primary steps to create a microstructure. 26 | 1) seed the domain with particles, 27 | 2) create a Voronoi power diagram, and 28 | 3) convert the diagram into an unstructured mesh. 29 | 30 | 31 | Examples 32 | -------- 33 | 34 | These images were created using MicroStructPy. 35 | For more examples, see the :ref:`examples_page` section. 36 | 37 | .. figure:: ../../src/microstructpy/examples/welcome_examples.png 38 | :alt: Several examples created using MicroStructPy. 39 | 40 | Examples created using MicroStructPy. 41 | 42 | 43 | Quick Start 44 | ----------- 45 | 46 | To install MicroStructPy, download it from PyPI using:: 47 | 48 | pip install microstructpy 49 | 50 | If there is an error with the install, try ``pip install pybind11`` first, 51 | then install MicroStructPy. 52 | This will create a command line executable and python package both 53 | named ``microstructpy``. 54 | To use the command line interface, create a file called ``input.xml`` and copy 55 | this into it: 56 | 57 | .. literalinclude:: ../../src/microstructpy/examples/minimal.xml 58 | :language: xml 59 | 60 | Next, run the file from the command line:: 61 | 62 | microstructpy input.xml 63 | 64 | This will produce three text files and three image files: ``seeds.txt``, 65 | ``polymesh.txt``, ``trimesh.txt``, ``seeds.png``, ``polymesh.png``, and 66 | ``trimesh.png``. 67 | The text files contain all of the data related to the seed geometries and 68 | meshes. 69 | The image files contain: 70 | 71 | .. figure:: ../../src/microstructpy/examples/joined.png 72 | :alt: Seed geometries, polygonal mesh, and unstructured mesh for min. expl. 73 | 74 | The output plots are: 75 | 1) seed geometries, 2) polygonal mesh, and 3) triangular mesh. 76 | 77 | 78 | The same results can be produced using this script: 79 | 80 | .. code-block:: python 81 | 82 | import matplotlib.pyplot as plt 83 | import microstructpy as msp 84 | 85 | 86 | phase = {'shape': 'circle', 'size': 0.15} 87 | domain = msp.geometry.Square() 88 | 89 | # Unpositioned list of seeds 90 | seeds = msp.seeding.SeedList.from_info(phase, domain.area) 91 | 92 | # Position seeds in domain 93 | seeds.position(domain) 94 | 95 | # Create polygonal mesh 96 | polygon_mesh = msp.meshing.PolyMesh.from_seeds(seeds, domain) 97 | 98 | # Create triangular mesh 99 | triangle_mesh = msp.meshing.TriMesh.from_polymesh(polygon_mesh) 100 | 101 | # Plot outputs 102 | for output in [seeds, polygon_mesh, triangle_mesh]: 103 | plt.figure() 104 | output.plot(edgecolor='k') 105 | plt.axis('image') 106 | plt.axis([-0.5, 0.5, -0.5, 0.5]) 107 | plt.show() 108 | 109 | 110 | .. include:: ../../README.rst 111 | :start-after: begin-publications 112 | :end-before: end-publications 113 | 114 | 115 | License and Attribution 116 | ----------------------- 117 | 118 | MicroStructPy is open source and freely available. 119 | Copyright for MicroStructPy is held by Georgia Tech Research Corporation. 120 | MicroStructPy is a major part of Kenneth (Kip) Hart's doctoral thesis, 121 | advised by Prof. Julian Rimoli. 122 | 123 | .. only:: latex 124 | 125 | .. topic:: License 126 | 127 | .. include:: ../../LICENSE.rst 128 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aabbtree==2.5.0 2 | matplotlib>=3.7.3 3 | pybind11==2.4.3 4 | pygmsh==7.1.17 5 | MeshPy==2022.1.3 6 | numpy>=1.24.4,<2.0 7 | pyquaternion==0.9.5 8 | pyvoro-mmalahe==1.3.4 9 | scipy>=1.10.1 10 | setuptools>=70.0.0 11 | xmltodict==0.12.0 12 | tox==3.14.0 13 | lsq-ellipse==2.0.1 14 | zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability 15 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | 4 | [tool:pytest] 5 | testpaths = tests 6 | 7 | python_files = 8 | test_*.py 9 | *_test.py 10 | tests.py 11 | 12 | addopts = 13 | -ra 14 | --strict 15 | --doctest-modules 16 | --doctest-glob=\*.rst 17 | --tb=short 18 | 19 | [isort] 20 | force_single_line = True 21 | line_length = 120 22 | known_first_party = microstructpy 23 | default_section = THIRDPARTY 24 | forced_separate = test_cli 25 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | from __future__ import absolute_import 4 | from __future__ import print_function 5 | 6 | from glob import glob 7 | from os.path import basename 8 | from os.path import dirname 9 | from os.path import join 10 | from os.path import splitext 11 | 12 | from setuptools import find_packages 13 | from setuptools import setup 14 | 15 | desc = 'Microstructure modeling, mesh generation, analysis, and visualization.' 16 | 17 | 18 | def read(*fname): 19 | return open(join(dirname(__file__), *fname)).read() 20 | 21 | 22 | def find_version(*fname): 23 | ver_str = '' 24 | for line in read(*fname).split('\n'): 25 | if line.startswith('__version__') and '=' in line: 26 | ver_str = line.split('=')[-1].strip().strip('\"').strip('\'') 27 | break 28 | return ver_str 29 | 30 | 31 | setup( 32 | name='microstructpy', 33 | version=find_version('src', 'microstructpy', '__init__.py'), 34 | license='MIT License', 35 | description=desc, 36 | long_description=read('README.rst'), 37 | long_description_content_type=' text/x-rst', 38 | author='Kenneth (Kip) Hart', 39 | author_email='kiphart91@gmail.com', 40 | url='https://github.com/kip-hart/MicroStructPy', 41 | project_urls={ 42 | 'Documentation': 'https://docs.microstructpy.org', 43 | }, 44 | packages=find_packages('src'), 45 | package_dir={'': 'src'}, 46 | py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], 47 | package_data={'': ['src/microstructpy/examples/*.{csv,py,xml}', 48 | 'src/microstructpy/examples/aluminum_micro.png']}, 49 | include_package_data=True, 50 | zip_safe=False, 51 | classifiers=[ 52 | # complete classifier list: 53 | # http://pypi.python.org/pypi?%3Aaction=list_classifiers 54 | 'Development Status :: 5 - Production/Stable', 55 | 'Intended Audience :: Science/Research', 56 | 'License :: OSI Approved :: MIT License', 57 | 'Operating System :: MacOS :: MacOS X', 58 | 'Operating System :: Microsoft :: Windows', 59 | 'Operating System :: POSIX :: Linux', 60 | 'Programming Language :: Python :: 3.9', 61 | 'Programming Language :: Python :: 3.10', 62 | 'Programming Language :: Python :: 3.11', 63 | 'Programming Language :: Python :: 3.12', 64 | 'Topic :: Scientific/Engineering', 65 | 'Topic :: Scientific/Engineering :: Mathematics', 66 | 'Topic :: Scientific/Engineering :: Physics' 67 | ], 68 | keywords=[ 69 | 'microstructure', 70 | 'micromechanics', 71 | 'finite element', 72 | 'FEM', 'FEA', 73 | 'mesh', 74 | 'polycrystal', 75 | 'tessellation', 76 | 'Laguerre tessellation', 77 | 'multi-sphere' 78 | ], 79 | install_requires=[ 80 | 'aabbtree>=2.5.0', 81 | 'pybind11', # must come before meshpy for successful install 82 | 'lsq-ellipse', 83 | 'matplotlib>=3.4.0', 84 | 'meshpy>=2018.2.1', 85 | 'numpy>=1.22.2', 86 | 'pygmsh>=7.0.2', 87 | 'pyquaternion', 88 | 'pyvoro-mmalahe>=1.3.4', # install issue with pyvoro 89 | 'scipy', 90 | 'xmltodict' 91 | ], 92 | entry_points={ 93 | 'console_scripts': [ 94 | 'microstructpy = microstructpy.cli:main', 95 | ] 96 | }, 97 | ) 98 | -------------------------------------------------------------------------------- /src/microstructpy/__init__.py: -------------------------------------------------------------------------------- 1 | import microstructpy.cli 2 | import microstructpy.geometry 3 | import microstructpy.meshing 4 | import microstructpy.seeding 5 | import microstructpy.verification 6 | 7 | __version__ = '1.5.9' 8 | -------------------------------------------------------------------------------- /src/microstructpy/_misc.py: -------------------------------------------------------------------------------- 1 | """Miscellaneous functions 2 | 3 | This private module contains miscellaneous functions. 4 | """ 5 | 6 | import ast 7 | 8 | import numpy as np 9 | 10 | __author__ = 'Kenneth (Kip) Hart' 11 | 12 | kw_solid = {'crystalline', 'granular', 'solid'} 13 | kw_amorph = {'amorphous', 'glass', 'matrix'} 14 | kw_void = {'void', 'crack', 'hole'} 15 | 16 | ori_kws = {'orientation', 'matrix', 'angle', 'angle_deg', 'angle_rad', 17 | 'rot_seq', 'rot_seq_rad', 'rot_seq_deg'} 18 | gen_kws = {'material_type', 'fraction', 'shape', 'name', 'color', 'position'} 19 | 20 | demo_needs = {'basalt_circle.xml': ['aphanitic_cdf.csv', 'olivine_cdf.csv'], 21 | 'from_image.py': ['aluminum_micro.png']} 22 | 23 | mpl_plural_kwargs = {'edgecolors', 'facecolors', 'linewidths', 'antialiaseds', 24 | 'offsets'} 25 | plt_3d_adj = { 26 | 'left': 0.4, 27 | 'right': 1, 28 | 'bottom': 0, 29 | 'top': 0.8, 30 | } 31 | 32 | 33 | # --------------------------------------------------------------------------- # 34 | # # 35 | # Convert String to Value (Infer Type) # 36 | # # 37 | # --------------------------------------------------------------------------- # 38 | def from_str(string): 39 | """ Convert string to number 40 | 41 | This function takes a string and converts it into a number or a list. 42 | 43 | Args: 44 | string (str): The string. 45 | 46 | Returns: 47 | The value in the string. 48 | 49 | """ 50 | s = string.strip() 51 | try: 52 | val = ast.literal_eval(s) 53 | except (ValueError, SyntaxError): 54 | if 'true' in s.lower(): 55 | tmp_s = s.lower().replace('true', 'True') 56 | tmp_val = from_str(tmp_s) 57 | if tmp_val != tmp_s: 58 | val = tmp_val 59 | else: 60 | val = s 61 | elif 'false' in s.lower(): 62 | tmp_s = s.lower().replace('false', 'False') 63 | tmp_val = from_str(tmp_s) 64 | if tmp_val != tmp_s: 65 | val = tmp_val 66 | else: 67 | val = s 68 | else: 69 | val = s 70 | return val 71 | 72 | 73 | # --------------------------------------------------------------------------- # 74 | # # 75 | # Tangent Spheres # 76 | # # 77 | # --------------------------------------------------------------------------- # 78 | def tangent_sphere(points, radii=None, simplices=None): 79 | """Calculate center and radius of tangent sphere(s) 80 | 81 | This function computes the center and radius of an n-dimensional sphere 82 | that is tangent to (n+1) spheres. For example, in 2D this function 83 | computes the center and radius of a circle tangent to three other circles. 84 | 85 | The operation of this function can be vectorized using the ``simplices`` 86 | input. The simplices should be an Mx(n+1) list of indices of the points. 87 | The result is an Mx(n+1) numpy array, where the first n columns are the 88 | coordinates of the sphere center. The final column is the radius of the 89 | sphere. 90 | 91 | If no radii are specified, the results are circumspheres of the simplices 92 | (circumcircles in 2D). 93 | 94 | Radii at each point can be speficied. If no radii are given, then the 95 | results are circumspheres of the simplices (circumcircles in 2D). 96 | 97 | Args: 98 | points (list, tuple, numpy.ndarray): List of points. 99 | radii (list, tuple, numpy.ndarray): List of radii. *(optional)* 100 | simplices (list, tuple, numpy.ndarray): List of simplices. *(optional)* 101 | 102 | Returns: 103 | numpy.ndarray: The centers and radii of tangent spheres. 104 | 105 | """ 106 | # set radii 107 | if radii is None: 108 | radii = np.full(len(points), 0) 109 | 110 | # extract points 111 | if simplices is None: 112 | simplices = np.arange(len(points)).reshape(1, -1) 113 | 114 | pts = np.array(points)[simplices] 115 | rs = np.array(radii)[simplices] 116 | 117 | # define circle distances 118 | cs = np.sum(pts * pts, axis=-1) - rs * rs 119 | 120 | # matrix and vector quantities 121 | pos1 = pts[:, 0] 122 | r1 = rs[:, 0] 123 | A = pts[:, 1:] - pos1[:, np.newaxis, :] 124 | b = -1 * (rs[:, 1:] - r1[:, np.newaxis]) 125 | c = 0.5 * (cs[:, 1:] - cs[:, 0, np.newaxis]) 126 | 127 | # linear system coefficients 128 | alpha = np.linalg.solve(A, b) 129 | beta = np.linalg.solve(A, c) 130 | 131 | # quadratic equation in rc 132 | r_beta = beta - pos1 133 | C1 = np.sum(alpha * alpha, axis=-1) - 1 134 | C2 = np.sum(r_beta * alpha, axis=-1) - r1 135 | C3 = np.sum(r_beta * r_beta, axis=-1) - r1 * r1 136 | 137 | # solve for rc 138 | discr = C2 * C2 - C1 * C3 139 | rt_discr = np.sqrt(discr) 140 | rt_discr[discr < 0] = 0 141 | 142 | rc1 = (-C2 + rt_discr) / C1 143 | rc2 = (-C2 - rt_discr) / C1 144 | 145 | mask = np.abs(rc1) < np.abs(rc2) 146 | rc = rc2 147 | rc[mask] = rc1[mask] 148 | rc[discr < 0] = 0 149 | 150 | # solve for center position 151 | posc = alpha * rc[:, np.newaxis] + beta 152 | 153 | # return results 154 | spheres = np.hstack((posc, rc.reshape(-1, 1))) 155 | return np.squeeze(spheres) 156 | 157 | 158 | def axisEqual3D(ax): 159 | ax.set_aspect('equal') 160 | 161 | 162 | def ax_objects(ax): 163 | n = 0 164 | for att in ['collections', 'images', 'lines', 'patches', 'texts']: 165 | n += len(getattr(ax, att)) 166 | return n 167 | -------------------------------------------------------------------------------- /src/microstructpy/examples/.gitignore: -------------------------------------------------------------------------------- 1 | */* 2 | *.txt 3 | *.png 4 | !aluminum_micro.png 5 | from_image/aluminum_micro.png 6 | -------------------------------------------------------------------------------- /src/microstructpy/examples/aluminum_micro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kip-hart/MicroStructPy/59f6bb14de2364f943943ec327b5014c1f5bb81d/src/microstructpy/examples/aluminum_micro.png -------------------------------------------------------------------------------- /src/microstructpy/examples/aphanitic_cdf.csv: -------------------------------------------------------------------------------- 1 | 0.0131329,0.01533213559176235 2 | 0.0244043,0.052366065866209975 3 | 0.0378734,0.10664028994219119 4 | 0.0524219,0.16912000096172966 5 | 0.0658667,0.22292200530737463 6 | 0.078159,0.270757219379418 7 | 0.0882121,0.31252632948214387 8 | 0.102668,0.35461605029964904 9 | 0.114868,0.40079860122375904 10 | 0.130442,0.44146381748575336 11 | 0.143858,0.4682199164619496 12 | 0.156045,0.4963700404401114 13 | 0.168257,0.5226454904629255 14 | 0.17936,0.5468300134050428 15 | 0.193731,0.5746462325954049 16 | 0.207044,0.600186102906063 17 | 0.220469,0.6147083337501832 18 | 0.232676,0.6276799922488674 19 | 0.247096,0.6392477153562951 20 | 0.2712,0.671419103088213 21 | 0.282213,0.7115650573599237 22 | 0.295636,0.7335215361206971 23 | 0.308959,0.7516200656859109 24 | 0.31891,0.7701590164868207 25 | 0.333254,0.7917551921790197 26 | 0.346618,0.8065154544827832 27 | 0.358667,0.8305986992703492 28 | 0.372182,0.8392944450621487 29 | 0.384077,0.8660193175326374 30 | 0.397254,0.9053530877053354 31 | 0.408645,0.9151403067362659 32 | 0.423006,0.9254603215782193 33 | 0.460577,0.9365599254365803 34 | 0.472521,0.9639040536949486 35 | 0.511517,0.9714442205219028 36 | 0.560104,0.9804858511663381 37 | 0.596389,0.9999999999999998 -------------------------------------------------------------------------------- /src/microstructpy/examples/basalt_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Plagioclase 5 | 6 | norm 7 | 45.2 8 | 0.2 9 | 10 | 11 | cdf 12 | aphanitic_cdf.csv 13 | 14 | #BDBDBD 15 | 16 | 17 | 18 | Olivine 19 | ellipse 20 | 21 | norm 22 | 19.1 23 | 0.2 24 | 25 | 26 | cdf 27 | olivine_cdf.csv 28 | 29 | 30 | uniform 31 | 1.0 32 | 2.0 33 | 34 | 35 | uniform 36 | -90 37 | 180 38 | 39 | #99BA73 40 | 41 | 42 | 43 | Diopside 44 | 45 | norm 46 | 13.2 47 | 0.2 48 | 49 | 50 | cdf 51 | aphanitic_cdf.csv 52 | 53 | #709642 54 | 55 | 56 | 57 | Hypersthene 58 | 59 | norm 60 | 16.6 61 | 0.2 62 | 63 | 64 | cdf 65 | aphanitic_cdf.csv 66 | 67 | #876E59 68 | 69 | 70 | 71 | Magnetite 72 | 73 | norm 74 | 3.35 75 | 0.2 76 | 77 | 78 | cdf 79 | aphanitic_cdf.csv 80 | 81 | #6E6E6E 82 | 83 | 84 | 85 | Chromite 86 | 87 | norm 88 | 0.65 89 | 0.2 90 | 91 | 92 | cdf 93 | aphanitic_cdf.csv \ 94 | 95 | #545454 96 | 97 | 98 | 99 | Ilmenite 100 | 101 | norm 102 | 0.65 103 | 0.2 104 | 105 | 106 | cdf 107 | aphanitic_cdf.csv 108 | 109 | #6B6B6B 110 | 111 | 112 | 113 | Apatite 114 | 115 | norm 116 | 3.35 117 | 0.2 118 | 119 | 120 | 121 | cdf 122 | aphanitic_cdf.csv 123 | 124 | #ABA687 125 | 126 | 127 | 128 | circle 129 | 10 130 | 131 | 132 | 133 | basalt_circle 134 | 135 | png 136 | png 137 | png 138 | png 139 | 140 | 141 | False 142 | True 143 | True 144 | 145 | 0.01 146 | 20 147 | 0.05 148 | 149 | 150 | 0.2 151 | 152 | 153 | 0.2 154 | 155 | 156 | 0.1 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /src/microstructpy/examples/colormap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sphere 5 | 6 | uniform 7 | 1 8 | 2 9 | 10 | 11 | 12 | 13 | cube 14 | 15 15 | 16 | 17 | 18 | colormap 19 | 15 20 | seed number 21 | RdYlBu 22 | 23 | -------------------------------------------------------------------------------- /src/microstructpy/examples/docs_banner.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import os 4 | 5 | import numpy as np 6 | import scipy.stats 7 | from matplotlib import pyplot as plt 8 | 9 | import microstructpy as msp 10 | 11 | 12 | def main(): 13 | # Colors 14 | c1 = '#12C2E9' 15 | c2 = '#C471ED' 16 | c3 = '#F64F59' 17 | 18 | # Offset 19 | off = 1 20 | 21 | # Create Directory 22 | dirname = os.path.join(os.path.dirname(__file__), 'docs_banner') 23 | if not os.path.exists(dirname): 24 | os.makedirs(dirname) 25 | 26 | # Create Domain 27 | domain = msp.geometry.Rectangle(width=10, length=20) 28 | 29 | # Create Unpositioned Seeds 30 | phase2 = {'color': c1} 31 | ell_geom = msp.geometry.Ellipse(a=8, b=3) 32 | ell_seed = msp.seeding.Seed(ell_geom, phase=2) 33 | 34 | mu = 1 35 | bnd = 0.5 36 | d_dist = scipy.stats.uniform(loc=mu-bnd, scale=2*bnd) 37 | phase0 = {'color': c2, 'shape': 'circle', 'd': d_dist} 38 | phase1 = {'color': c3, 'shape': 'circle', 'd': d_dist} 39 | circle_area = domain.area - ell_geom.area 40 | seeds = msp.seeding.SeedList.from_info([phase0, phase1], circle_area) 41 | 42 | seeds.append(ell_seed) 43 | hold = [False for seed in seeds] 44 | hold[-1] = True 45 | phases = [phase0, phase1, phase2] 46 | 47 | # Create Positioned Seeds 48 | seeds.position(domain, hold=hold, verbose=True) 49 | 50 | # Create Polygonal Mesh 51 | pmesh = msp.meshing.PolyMesh.from_seeds(seeds, domain) 52 | 53 | # Create Triangular Mesh 54 | tmesh = msp.meshing.TriMesh.from_polymesh(pmesh, 55 | min_angle=12, 56 | max_edge_length=0.2, 57 | max_volume=0.4) 58 | 59 | # Create Figure 60 | k = 0.12 61 | len_x = 3 * domain.length + 4 * off 62 | len_y = domain.width + 2 * off 63 | fig = plt.figure(figsize=(k * len_x, k * len_y)) 64 | 65 | # Plot Seeds 66 | seed_colors = [phases[s.phase]['color'] for s in seeds] 67 | seeds.plot(color=seed_colors, alpha=0.8, edgecolor='k', linewidth=0.3) 68 | domain.plot(facecolor='none', edgecolor='k', linewidth=0.3) 69 | 70 | # Plot Polygonal Mesh 71 | pmesh.points = np.array(pmesh.points) 72 | pmesh.points[:, 0] += domain.length + off 73 | for region, phase_num in zip(pmesh.regions, pmesh.phase_numbers): 74 | if phase_num == 2: 75 | continue 76 | color = phases[phase_num]['color'] 77 | 78 | facets = [pmesh.facets[f] for f in region] 79 | kps = ordered_kps(facets) 80 | x, y = zip(*[pmesh.points[kp] for kp in kps]) 81 | plt.fill(x, y, color=color, alpha=0.8, edgecolor='none') 82 | 83 | ellipse_regions = set() 84 | for region_num, phase_num in enumerate(pmesh.phase_numbers): 85 | if phase_num == 2: 86 | ellipse_regions.add(region_num) 87 | 88 | ellipse_facets = [] 89 | for facet, neighbors in zip(pmesh.facets, pmesh.facet_neighbors): 90 | common_regions = ellipse_regions & set(neighbors) 91 | if len(common_regions) == 1: 92 | ellipse_facets.append(facet) 93 | ellipse_kps = ordered_kps(ellipse_facets) 94 | x, y = zip(*[pmesh.points[kp] for kp in ellipse_kps]) 95 | plt.fill(x, y, color=phases[-1]['color'], alpha=0.8, edgecolor='none') 96 | 97 | for facet, neighbors in zip(pmesh.facets, pmesh.facet_neighbors): 98 | common_regions = ellipse_regions & set(neighbors) 99 | if len(common_regions) < 2: 100 | x, y = zip(*[pmesh.points[kp] for kp in facet]) 101 | plt.plot(x, y, color='k', linewidth=0.3) 102 | 103 | # Plot Triangular Mesh 104 | tmesh.points = np.array(tmesh.points) 105 | tmesh.points[:, 0] += 2 * off + 2 * domain.length 106 | tri_colors = [seed_colors[n] for n in tmesh.element_attributes] 107 | tmesh.plot(color=tri_colors, alpha=0.8, edgecolor='k', linewidth=0.2) 108 | 109 | # Set Up Axes 110 | plt.gca().set_position([0, 0, 1, 1]) 111 | plt.axis('image') 112 | plt.gca().set_axis_off() 113 | plt.gca().get_xaxis().set_visible(False) 114 | plt.gca().get_yaxis().set_visible(False) 115 | 116 | xlim, ylim = domain.limits 117 | xlim[0] -= off 118 | xlim[1] += 3 * off + 2 * domain.length 119 | 120 | ylim[0] -= off 121 | ylim[1] += off 122 | 123 | plt.axis(list(xlim) + list(ylim)) 124 | 125 | fname = os.path.join(dirname, 'banner.png') 126 | plt.savefig(fname, bbox_inches='tight', pad_inches=0) 127 | plt.savefig(fname.replace('.png', '.pdf'), bbox_inches='tight', pad_inches=0) 128 | 129 | 130 | def ordered_kps(pairs): 131 | t_pairs = [tuple(p) for p in pairs] 132 | kps = list(t_pairs.pop()) 133 | while t_pairs: 134 | for i, pair in enumerate(t_pairs): 135 | if kps[-1] in pair: 136 | break 137 | assert kps[-1] in pair, pairs 138 | kps += [kp for kp in t_pairs.pop(i) if kp != kps[-1]] 139 | return kps[:-1] 140 | 141 | 142 | if __name__ == '__main__': 143 | main() 144 | -------------------------------------------------------------------------------- /src/microstructpy/examples/elliptical_grains.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2 5 | ellipse 6 | 7 | uniform 8 | 0.2 9 | 0.35 10 | 11 | 0.05 12 | 13 | uniform 14 | 0 15 | 20 16 | 17 | orange 18 | 19 | 20 | 21 | 1 22 | circle 23 | 24 | lognorm 25 | 0.004 26 | 1.0 27 | 28 | plum 29 | 30 | 31 | 32 | 1 33 | circle 34 | 35 | lognorm 36 | 0.004 37 | 1.0 38 | 39 | lightblue 40 | 41 | 42 | 43 | 1 44 | circle 45 | 46 | lognorm 47 | 0.004 48 | 1.0 49 | 50 | lightgreen 51 | 52 | 53 | 54 | 1 55 | circle 56 | 57 | lognorm 58 | 0.004 59 | 1.0 60 | 61 | khaki 62 | 63 | 64 | 65 | rectangle 66 | (2.4, 1.2) 67 | 68 | 69 | 70 | True 71 | 20 72 | 0.01 73 | 0.004 74 | 75 | elliptical_grains 76 | False 77 | 78 | 79 | 1.0 80 | 81 | 82 | 83 | 1.0 84 | 85 | 86 | 87 | 0.1 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/microstructpy/examples/foam.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import numpy as np 4 | import scipy.stats 5 | from matplotlib import pyplot as plt 6 | 7 | import microstructpy as msp 8 | 9 | 10 | def main(): 11 | # Create Directory 12 | dirname = os.path.join(os.path.dirname(__file__), 'foam') 13 | if not os.path.exists(dirname): 14 | os.makedirs(dirname) 15 | 16 | # Define Domain 17 | domain = msp.geometry.Square(side_length=8) 18 | 19 | # Create Void Tessellation 20 | void_mat = {'material_type': 'void', 21 | 'shape': 'circle', 22 | 'size': scipy.stats.lognorm(scale=1, s=0.2) 23 | } 24 | 25 | void_a = 0.7 * domain.area 26 | void_seeds = msp.seeding.SeedList.from_info(void_mat, void_a) 27 | void_seeds.position(domain, rtol=0.03, verbose=True) 28 | void_tess = msp.meshing.PolyMesh.from_seeds(void_seeds, domain) 29 | 30 | # Add Foam 31 | foam_mat = {'material_type': 'amorphous', 32 | 'shape': 'circle', 33 | 'size': scipy.stats.lognorm(scale=0.15, s=0.1) 34 | } 35 | 36 | foam_a = 0.15 * domain.area 37 | foam_seeds = msp.seeding.SeedList.from_info(foam_mat, foam_a) 38 | inds = np.flip(np.argsort([s.volume for s in foam_seeds])) 39 | foam_seeds = foam_seeds[inds] 40 | 41 | bkdwns = np.array([s.breakdown[0] for s in foam_seeds]) 42 | np.random.seed(0) 43 | for i, seed in enumerate(foam_seeds): 44 | if i == 0: 45 | trial_pt = trial_position(void_tess) 46 | else: 47 | r = seed.geometry.r 48 | check_bkdwns = bkdwns[:i] 49 | good_pt = False 50 | while not good_pt: 51 | trial_pt = trial_position(void_tess) 52 | good_pt = check_pt(trial_pt, r, check_bkdwns) 53 | 54 | seed.position = trial_pt 55 | bkdwns[i] = seed.breakdown 56 | seed.phase = 1 57 | 58 | # Combine Results 59 | materials = [void_mat, foam_mat] 60 | seeds = void_seeds + foam_seeds 61 | pmesh = msp.meshing.PolyMesh.from_seeds(seeds, domain) 62 | 63 | # Triangular Mesh 64 | tmesh = msp.meshing.TriMesh.from_polymesh(pmesh, 65 | materials, 66 | min_angle=20, 67 | max_edge_length=0.1) 68 | 69 | # Plot 70 | tmesh.plot(facecolor='aquamarine', 71 | edgecolor='k', 72 | linewidth=0.2) 73 | 74 | plt.gca().set_position([0, 0, 1, 1]) 75 | plt.axis('image') 76 | plt.gca().set_axis_off() 77 | plt.gca().get_xaxis().set_visible(False) 78 | plt.gca().get_yaxis().set_visible(False) 79 | 80 | xlim, ylim = domain.limits 81 | plt.axis([xlim[0], xlim[1], ylim[0], ylim[1]]) 82 | 83 | for ext in ['png', 'pdf']: 84 | fname = os.path.join(dirname, 'trimesh.' + ext) 85 | plt.savefig(fname, bbox_inches='tight', pad_inches=0) 86 | 87 | 88 | def pick_edge(void_tess): 89 | f_neighs = void_tess.facet_neighbors 90 | i = -1 91 | neighs = [-1, -1] 92 | while any([n < 0 for n in neighs]): 93 | i = np.random.randint(len(f_neighs)) 94 | neighs = f_neighs[i] 95 | facet = void_tess.facets[i] 96 | j = np.random.randint(len(facet)) 97 | kp1 = facet[j] 98 | kp2 = facet[j - 1] 99 | return kp1, kp2 100 | 101 | 102 | def trial_position(void_tess): 103 | kp1, kp2 = pick_edge(void_tess) 104 | pt1 = void_tess.points[kp1] 105 | pt2 = void_tess.points[kp2] 106 | 107 | f = np.random.rand() 108 | return [f * x1 + (1 - f) * x2 for x1, x2 in zip(pt1, pt2)] 109 | 110 | 111 | def check_pt(point, r, breakdowns): 112 | pts = breakdowns[:, :-1] 113 | rads = breakdowns[:, -1] 114 | 115 | rel_pos = pts - point 116 | dist = np.sqrt(np.sum(rel_pos * rel_pos, axis=1)) 117 | min_dist = rads + r - 0.3 * np.minimum(rads, r) 118 | return np.all(dist > min_dist) 119 | 120 | 121 | if __name__ == '__main__': 122 | main() 123 | -------------------------------------------------------------------------------- /src/microstructpy/examples/from_image.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | import numpy as np 5 | from matplotlib import image as mpim 6 | from matplotlib import pyplot as plt 7 | 8 | import microstructpy as msp 9 | 10 | # Read in image 11 | image_basename = 'aluminum_micro.png' 12 | image_path = os.path.dirname(__file__) 13 | image_filename = os.path.join(image_path, image_basename) 14 | image = mpim.imread(image_filename) 15 | im_brightness = image[:, :, 0] 16 | 17 | # Bin the pixels 18 | br_bins = [0.00, 0.50, 1.00] 19 | 20 | bin_nums = np.zeros_like(im_brightness, dtype='int') 21 | for i in range(len(br_bins) - 1): 22 | lb = br_bins[i] 23 | ub = br_bins[i + 1] 24 | mask = np.logical_and(im_brightness >= lb, im_brightness <= ub) 25 | bin_nums[mask] = i 26 | 27 | # Define the phases 28 | phases = [{'color': c, 'material_type': 'amorphous'} for c in ('C0', 'C1')] 29 | 30 | # Create the polygon mesh 31 | m, n = bin_nums.shape 32 | x = np.arange(n + 1).astype('float') 33 | y = m + 1 - np.arange(m + 1).astype('float') 34 | xx, yy = np.meshgrid(x, y) 35 | pts = np.array([xx.flatten(), yy.flatten()]).T 36 | kps = np.arange(len(pts)).reshape(xx.shape) 37 | 38 | n_facets = 2 * (m + m * n + n) 39 | n_regions = m * n 40 | facets = np.full((n_facets, 2), -1) 41 | regions = np.full((n_regions, 4), 0) 42 | region_phases = np.full(n_regions, 0) 43 | 44 | facet_top = np.full((m, n), -1, dtype='int') 45 | facet_bottom = np.full((m, n), -1, dtype='int') 46 | facet_left = np.full((m, n), -1, dtype='int') 47 | facet_right = np.full((m, n), -1, dtype='int') 48 | 49 | k_facets = 0 50 | k_regions = 0 51 | for i in range(m): 52 | for j in range(n): 53 | kp_top_left = kps[i, j] 54 | kp_bottom_left = kps[i + 1, j] 55 | kp_top_right = kps[i, j + 1] 56 | kp_bottom_right = kps[i + 1, j + 1] 57 | 58 | # left facet 59 | if facet_left[i, j] < 0: 60 | fnum_left = k_facets 61 | facets[fnum_left] = (kp_top_left, kp_bottom_left) 62 | k_facets += 1 63 | 64 | if j > 0: 65 | facet_right[i, j - 1] = fnum_left 66 | else: 67 | fnum_left = facet_left[i, j] 68 | 69 | # right facet 70 | if facet_right[i, j] < 0: 71 | fnum_right = k_facets 72 | facets[fnum_right] = (kp_top_right, kp_bottom_right) 73 | k_facets += 1 74 | 75 | if j + 1 < n: 76 | facet_left[i, j + 1] = fnum_right 77 | else: 78 | fnum_right = facet_right[i, j] 79 | 80 | # top facet 81 | if facet_top[i, j] < 0: 82 | fnum_top = k_facets 83 | facets[fnum_top] = (kp_top_left, kp_top_right) 84 | k_facets += 1 85 | 86 | if i > 0: 87 | facet_bottom[i - 1, j] = fnum_top 88 | else: 89 | fnum_top = facet_top[i, j] 90 | 91 | # bottom facet 92 | if facet_bottom[i, j] < 0: 93 | fnum_bottom = k_facets 94 | facets[fnum_bottom] = (kp_bottom_left, kp_bottom_right) 95 | k_facets += 1 96 | 97 | if i + 1 < m: 98 | facet_top[i + 1, j] = fnum_bottom 99 | else: 100 | fnum_bottom = facet_bottom[i, j] 101 | 102 | # update region 103 | region = (fnum_top, fnum_left, fnum_bottom, fnum_right) 104 | regions[k_regions] = region 105 | region_phases[k_regions] = bin_nums[i, j] 106 | k_regions += 1 107 | 108 | 109 | pmesh = msp.meshing.PolyMesh(pts, facets, regions, 110 | seed_numbers=range(n_regions), 111 | phase_numbers=region_phases) 112 | 113 | # Create the triangle mesh 114 | tmesh = msp.meshing.TriMesh.from_polymesh(pmesh, phases=phases, min_angle=20) 115 | 116 | # Plot triangle mesh 117 | fig = plt.figure() 118 | ax = plt.Axes(fig, [0., 0., 1., 1.]) 119 | ax.set_axis_off() 120 | ax.get_xaxis().set_visible(False) 121 | ax.get_yaxis().set_visible(False) 122 | fig.add_axes(ax) 123 | 124 | fcs = [phases[region_phases[r]]['color'] for r in tmesh.element_attributes] 125 | tmesh.plot(facecolors=fcs, edgecolors='k', lw=0.2) 126 | 127 | 128 | plt.axis('square') 129 | plt.xlim(x.min(), x.max()) 130 | plt.ylim(y.min(), y.max()) 131 | plt.axis('off') 132 | 133 | # Save plot and copy input file 134 | plot_basename = 'from_image/trimesh.png' 135 | file_dir = os.path.dirname(os.path.realpath(__file__)) 136 | filename = os.path.join(file_dir, plot_basename) 137 | dirs = os.path.dirname(filename) 138 | if not os.path.exists(dirs): 139 | os.makedirs(dirs) 140 | plt.savefig(filename, bbox_inches='tight', pad_inches=0) 141 | 142 | shutil.copy(image_filename, dirs) 143 | -------------------------------------------------------------------------------- /src/microstructpy/examples/grain_neighborhoods.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import os 4 | 5 | import numpy as np 6 | import scipy.integrate 7 | import scipy.stats 8 | from matplotlib import pyplot as plt 9 | 10 | import microstructpy as msp 11 | 12 | # Define the domain 13 | domain = msp.geometry.Square(corner=(0, 0), side_length=10) 14 | 15 | # Define the material phases 16 | a_dist = scipy.stats.lognorm(s=1, scale=0.1) 17 | matrix_phase = {'fraction': 1, 18 | 'material_type': 'matrix', 19 | 'shape': 'circle', 20 | 'area': a_dist} 21 | 22 | neighborhood_phase = {'fraction': 1, 23 | 'material_type': 'solid', 24 | 'shape': 'ellipse', 25 | 'a': 1.5, 26 | 'b': 0.6, 27 | 'angle_deg': scipy.stats.uniform(0, 360)} 28 | 29 | phases = [matrix_phase, neighborhood_phase] 30 | 31 | # Create the seed list 32 | seeds = msp.seeding.SeedList.from_info(phases, domain.area) 33 | seeds.position(domain) 34 | 35 | # Replace the neighborhood phase with materials 36 | a = neighborhood_phase['a'] 37 | b = neighborhood_phase['b'] 38 | r = b / 3 39 | n = 16 40 | 41 | t_perim = np.linspace(0, 2 * np.pi, 201) 42 | x_perim = (a - r) * np.cos(t_perim) 43 | y_perim = (b - r) * np.sin(t_perim) 44 | dx = np.insert(np.diff(x_perim), 0, 0) 45 | dy = np.insert(np.diff(y_perim), 0, 0) 46 | ds = np.sqrt(dx * dx + dy * dy) 47 | arc_len = scipy.integrate.cumulative_trapezoid(ds, x=t_perim, initial=0) 48 | eq_spaced = arc_len[-1] * np.arange(n) / n 49 | x_pts = np.interp(eq_spaced, arc_len, x_perim) 50 | y_pts = np.interp(eq_spaced, arc_len, y_perim) 51 | 52 | repl_seeds = msp.seeding.SeedList() 53 | geom = {'a': a - 2 * r, 'b': b - 2 * r} 54 | for sn, seed in enumerate(seeds): 55 | if seed.phase == 0: 56 | repl_seeds.append(seed) 57 | else: 58 | center = seed.position 59 | theta = seed.geometry.angle_rad 60 | 61 | geom['angle_rad'] = theta 62 | geom['center'] = center 63 | core_seed = msp.seeding.Seed.factory('ellipse', phase=3, 64 | position=seed.position, **geom) 65 | repl_seeds.append(core_seed) 66 | 67 | x_ring = center[0] + x_pts * np.cos(theta) - y_pts * np.sin(theta) 68 | y_ring = center[1] + x_pts * np.sin(theta) + y_pts * np.cos(theta) 69 | for i in range(n): 70 | phase = 1 + (i % 2) 71 | center = (x_ring[i], y_ring[i]) 72 | ring_geom = {'center': center, 'r': r} 73 | ring_seed = msp.seeding.Seed.factory('circle', position=center, 74 | phase=phase, **ring_geom) 75 | if domain.within(center): 76 | repl_seeds.append(ring_seed) 77 | 78 | # Create polygon and triangle meshes 79 | pmesh = msp.meshing.PolyMesh.from_seeds(repl_seeds, domain) 80 | phases = [{'material_type': 'solid'} for i in range(4)] 81 | phases[0]['material_type'] = 'matrix' 82 | tmesh = msp.meshing.TriMesh.from_polymesh(pmesh, phases, min_angle=20, 83 | max_volume=0.1) 84 | 85 | # Plot triangle mesh 86 | colors = ['C' + str(repl_seeds[att].phase) for att in tmesh.element_attributes] 87 | tmesh.plot(facecolors=colors, edgecolors='k', linewidth=0.2) 88 | 89 | plt.axis('square') 90 | plt.xlim(domain.limits[0]) 91 | plt.ylim(domain.limits[1]) 92 | 93 | file_dir = os.path.dirname(os.path.realpath(__file__)) 94 | filename = os.path.join(file_dir, 'grain_neighborhoods/trimesh.png') 95 | dirs = os.path.dirname(filename) 96 | if not os.path.exists(dirs): 97 | os.makedirs(dirs) 98 | plt.savefig(filename, bbox_inches='tight', pad_inches=0) 99 | -------------------------------------------------------------------------------- /src/microstructpy/examples/intro_1_basic.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Matrix 5 | matrix 6 | 2 7 | circle 8 | 9 | uniform 10 | 0 11 | 1.5 12 | 13 | 14 | 15 | 16 | Inclusions 17 | 1 18 | circle 19 | 2 20 | 21 | 22 | 23 | square 24 | 20 25 | (0, 0) 26 | 27 | 28 | 29 | True 30 | intro_1_basic 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/microstructpy/examples/intro_2_quality.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Matrix 5 | matrix 6 | 2 7 | 8 | circle 9 | 10 | uniform 11 | 0 12 | 1.5 13 | 14 | 15 | 16 | 17 | Inclusions 18 | 1 19 | circle 20 | 2 21 | 22 | 23 | 24 | square 25 | 20 26 | (0, 0) 27 | 28 | 29 | 30 | intro_2_quality 31 | True 32 | 33 | 34 | 25 35 | 1 36 | 0.1 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/microstructpy/examples/intro_3_size_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Matrix 5 | matrix 6 | 2 7 | 8 | circle 9 | 10 | uniform 11 | 0 12 | 1.5 13 | 14 | 15 | 16 | 17 | Inclusions 18 | 1 19 | ellipse 20 | 21 | triang 22 | 0 23 | 2 24 | 1 25 | 26 | 27 | uniform 28 | 1 29 | 2 30 | 31 | random 32 | 33 | 34 | 35 | square 36 | 20 37 | (0, 0) 38 | 39 | 40 | 41 | intro_3_size_shape 42 | True 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/microstructpy/examples/intro_4_oriented.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Matrix 5 | matrix 6 | 2 7 | 8 | circle 9 | 10 | uniform 11 | 0 12 | 1.5 13 | 14 | 15 | 16 | 17 | Inclusions 18 | 1 19 | ellipse 20 | 21 | triang 22 | 0 23 | 2 24 | 1 25 | 26 | 27 | uniform 28 | 1 29 | 2 30 | 31 | 32 | uniform 33 | -10 34 | 20 35 | 36 | 37 | 38 | 39 | square 40 | 20 41 | (0, 0) 42 | 43 | 44 | 45 | intro_4_oriented 46 | True 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/microstructpy/examples/intro_5_plotting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | matrix 5 | 2 6 | 7 | circle 8 | 9 | uniform 10 | 0 11 | 1.5 12 | 13 | pink 14 | 15 | 16 | 17 | 1 18 | circle 19 | 2 20 | lime 21 | 22 | 23 | 24 | square 25 | 20 26 | (0, 0) 27 | 28 | 29 | 30 | 31 | png 32 | png, pdf 33 | png 34 | eps 35 | pdf 36 | 37 | 38 | intro_5_plotting 39 | True 40 | 41 | 42 | 0.5 43 | none 44 | 45 | 46 | 47 | 3 48 | #A4058F 49 | 50 | 51 | 52 | 0.2 53 | navy 54 | 55 | 56 | False 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/microstructpy/examples/intro_6_culmination.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | matrix 5 | 2 6 | 7 | circle 8 | 9 | uniform 10 | 0 11 | 1.5 12 | 13 | pink 14 | 15 | 16 | 17 | 1 18 | ellipse 19 | 20 | triang 21 | 0 22 | 2 23 | 1 24 | 25 | 26 | uniform 27 | 1 28 | 2 29 | 30 | 31 | uniform 32 | -10 33 | 20 34 | 35 | lime 36 | 37 | 38 | 39 | square 40 | 20 41 | (0, 0) 42 | 43 | 44 | 45 | 46 | png 47 | png, pdf 48 | png 49 | eps 50 | pdf 51 | 52 | 53 | intro_6_culmination 54 | True 55 | 56 | 25 57 | 1 58 | 0.1 59 | 60 | 61 | 0.5 62 | none 63 | 64 | 65 | 66 | 3 67 | #A4058F 68 | 69 | 70 | 71 | 0.2 72 | navy 73 | 74 | 75 | False 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/microstructpy/examples/logo.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import os 4 | 5 | import numpy as np 6 | from matplotlib import collections 7 | from matplotlib import pyplot as plt 8 | from matplotlib.backends.backend_agg import FigureCanvasAgg 9 | from matplotlib.offsetbox import AnnotationBbox 10 | from matplotlib.offsetbox import OffsetImage 11 | 12 | import microstructpy as msp 13 | 14 | 15 | def main(n_seeds, size_rng, pos_rng, k_lw): 16 | bkgrnd_color = 'black' 17 | line_color = (1, 1, 1, 1) # white 18 | 19 | dpi = 300 20 | init_size = 2000 21 | logo_size = 1500 22 | favicon_size = 48 23 | 24 | logo_basename = 'logo.svg' 25 | favicon_basename = 'favicon.ico' 26 | social_basename = 'social.png' 27 | file_dir = os.path.dirname(os.path.realpath(__file__)) 28 | path = os.path.join(file_dir, 'logo') 29 | if not os.path.exists(path): 30 | os.makedirs(path) 31 | logo_filename = os.path.join(path, logo_basename) 32 | pad_filename = os.path.join(path, 'pad_' + logo_basename) 33 | favicon_filename = os.path.join(path, favicon_basename) 34 | social_filename = os.path.join(path, social_basename) 35 | 36 | # Set Domain 37 | domain = msp.geometry.Circle() 38 | 39 | # Set Seed List 40 | np.random.seed(size_rng) 41 | rs = 0.3 * np.random.rand(n_seeds) 42 | 43 | factory = msp.seeding.Seed.factory 44 | seeds = msp.seeding.SeedList([factory('circle', r=r) for r in rs]) 45 | seeds.position(domain, rng_seed=pos_rng) 46 | 47 | # Create the Poly Mesh 48 | pmesh = msp.meshing.PolyMesh.from_seeds(seeds, domain) 49 | 50 | # Create and Format the Figure 51 | plt.clf() 52 | plt.close('all') 53 | fig = plt.figure(figsize=(init_size / dpi, init_size / dpi), dpi=dpi) 54 | ax = plt.Axes(fig, [0., 0., 1., 1.]) 55 | ax.set_axis_off() 56 | ax.get_xaxis().set_visible(False) 57 | ax.get_yaxis().set_visible(False) 58 | fig.add_axes(ax) 59 | 60 | # Plot the Domain 61 | domain.plot(ec='none', fc=bkgrnd_color) 62 | 63 | # Plot the Facets 64 | facet_colors = [] 65 | for neigh_pair in pmesh.facet_neighbors: 66 | if min(neigh_pair) < 0: 67 | facet_colors.append((0, 0, 0, 0)) 68 | else: 69 | facet_colors.append(line_color) 70 | 71 | lw = k_lw * init_size / 100 72 | pmesh.plot_facets(index_by='facet', colors=facet_colors, 73 | linewidth=lw, capstyle='round') 74 | 75 | pts = np.array(pmesh.points) 76 | rs = np.sqrt(np.sum(pts * pts, axis=1)) 77 | mask = np.isclose(rs, 1) 78 | 79 | edges = [] 80 | for facet in pmesh.facets: 81 | if np.sum(mask[facet]) != 1: 82 | continue 83 | 84 | edge = np.copy(pts[facet]) 85 | if mask[facet[0]]: 86 | u = edge[0] - edge[1] 87 | u *= 1.1 88 | edge[0] = edge[1] + u 89 | else: 90 | u = edge[1] - edge[0] 91 | u *= 1.1 92 | edge[1] = edge[0] + u 93 | edges.append(edge) 94 | 95 | pc = collections.LineCollection(edges, color=line_color, linewidth=lw, 96 | capstyle='round') 97 | ax.add_collection(pc) 98 | 99 | # Format the Plot and Convert to Image Array 100 | plt.axis('square') 101 | plt.axis(1.01 * np.array([-1, 1, -1, 1])) 102 | canvas = FigureCanvasAgg(fig) 103 | canvas.draw() 104 | 105 | plt_im = np.array(canvas.buffer_rgba()) 106 | mask = plt_im[:, :, 0] > 0.5 * 255 107 | 108 | # Create the Logo 109 | logo_im = np.copy(plt_im) 110 | 111 | xx, yy = np.meshgrid(*[np.arange(n) for n in logo_im.shape[:2]]) 112 | zz = - 0.2 * xx + 0.9 * yy 113 | ss = (zz - zz.min()) / (zz.max() - zz.min()) 114 | 115 | c1 = [67, 206, 162] 116 | c2 = [24, 90, 157] 117 | 118 | logo_im[mask, -1] = 0 # transparent background 119 | 120 | # gradient 121 | for i in range(logo_im.shape[-1] - 1): 122 | logo_im[~mask, i] = (1 - ss[~mask]) * c1[i] + ss[~mask] * c2[i] 123 | 124 | inds = np.linspace(0, logo_im.shape[0] - 1, logo_size).astype('int') 125 | logo_im = logo_im[inds] 126 | logo_im = logo_im[:, inds] 127 | 128 | pad_w = logo_im.shape[0] 129 | pad_h = 0.5 * logo_im.shape[1] 130 | pad_shape = np.array([pad_w, pad_h, logo_im.shape[2]]).astype('int') 131 | logo_pad = np.zeros(pad_shape, dtype=logo_im.dtype) 132 | pad_im = np.concatenate((logo_pad, logo_im, logo_pad), axis=1) 133 | doc_im = np.concatenate((logo_pad, pad_im, logo_pad), axis=1) 134 | 135 | plt.imsave(logo_filename, logo_im, dpi=dpi) 136 | plt.imsave(logo_filename.replace('.svg', '.png'), np.ascontiguousarray(logo_im), dpi=dpi) 137 | plt.imsave(pad_filename, pad_im, dpi=dpi) 138 | plt.imsave(pad_filename.replace('.svg', '.png'), np.ascontiguousarray(doc_im), dpi=dpi) 139 | 140 | # Create the Favicon 141 | fav_im = np.copy(logo_im) 142 | inds = np.linspace(0, fav_im.shape[0] - 1, favicon_size).astype('int') 143 | fav_im = fav_im[inds] 144 | fav_im = fav_im[:, inds] 145 | 146 | plt.imsave(favicon_filename, np.ascontiguousarray(fav_im), dpi=dpi, format='png') 147 | 148 | # Create the Social Banner 149 | fig_social, ax_social = plt.subplots() 150 | 151 | ax_social.set_xlim(0, 2) 152 | ax_social.set_ylim(0, 1) 153 | ax_social.set_aspect('equal') 154 | 155 | ax_social.set_axis_off() 156 | ax_social.get_xaxis().set_visible(False) 157 | ax_social.get_yaxis().set_visible(False) 158 | 159 | imagebox = OffsetImage(logo_im, zoom=0.05) 160 | ab = AnnotationBbox(imagebox, (1, 0.7), frameon=False) 161 | ax_social.add_artist(ab) 162 | ax_social.text(1, 0.35, 'MicroStructPy', 163 | fontsize=20, 164 | weight='bold', 165 | horizontalalignment='center', 166 | verticalalignment='center') 167 | ax_social.text(1, 0.23, 'Microstructure Mesh Generation in Python', 168 | fontsize=10, 169 | horizontalalignment='center', 170 | verticalalignment='center') 171 | plt.draw() 172 | plt.savefig(social_filename, bbox_inches='tight', pad_inches=0) 173 | plt.close('all') 174 | 175 | 176 | if __name__ == '__main__': 177 | n_seeds = 14 178 | size_rng = 4 179 | pos_rng = 7 180 | k_lw = 1.1 181 | 182 | main(n_seeds, size_rng, pos_rng, k_lw) 183 | -------------------------------------------------------------------------------- /src/microstructpy/examples/minimal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | circle 5 | 0.15 6 | 7 | 8 | 9 | square 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/microstructpy/examples/minimal_paired.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | circle 5 | 0.09 6 | 7 | 8 | 9 | square 10 | 11 | 12 | 13 | minimal 14 | False 15 | seed number 16 | Paired 17 | gmsh 18 | 0.03 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/microstructpy/examples/olivine_cdf.csv: -------------------------------------------------------------------------------- 1 | 0.0578778,1.5701240548769328e-6 2 | 0.154341,0.00033025723859269246 3 | 0.250804,0.0036920787776204785 4 | 0.361736,0.009699296325930005 5 | 0.458199,0.022361588309627884 6 | 0.559486,0.03540695717145988 7 | 0.660772,0.05480969583418152 8 | 0.757235,0.06994861116131441 9 | 0.858521,0.09885177611031959 10 | 0.959807,0.12608273026813632 11 | 1.05145,0.17472554028359996 12 | 1.1672,0.19938391704059527 13 | 1.25402,0.2623745913809862 14 | 1.36013,0.329340547069217 15 | 1.46624,0.36327522429202896 16 | 1.5627,0.42510214002218444 17 | 1.65434,0.5226136019375014 18 | 1.76045,0.5524047674579283 19 | 1.86174,0.5876397044607361 20 | 1.96302,0.6289435590528172 21 | 2.35852,0.7005801812903562 22 | 2.96141,0.8423918690211719 23 | 3.06752,1.0 24 | -------------------------------------------------------------------------------- /src/microstructpy/examples/standard_voronoi.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from matplotlib import pyplot as plt 4 | 5 | import microstructpy as msp 6 | 7 | # Create domain 8 | domain = msp.geometry.Square() 9 | 10 | # Create list of seed points 11 | factory = msp.seeding.Seed.factory 12 | n = 50 13 | seeds = msp.seeding.SeedList([factory('circle', r=0.01) for i in range(n)]) 14 | seeds.position(domain) 15 | 16 | # Create Voronoi diagram 17 | pmesh = msp.meshing.PolyMesh.from_seeds(seeds, domain) 18 | 19 | # Plot Voronoi diagram and seed points 20 | pmesh.plot(edgecolors='k', facecolors='gray') 21 | seeds.plot(edgecolors='k', facecolors='none') 22 | 23 | plt.axis('square') 24 | plt.xlim(domain.limits[0]) 25 | plt.ylim(domain.limits[1]) 26 | 27 | file_dir = os.path.dirname(os.path.realpath(__file__)) 28 | filename = os.path.join(file_dir, 'standard_voronoi/voronoi_diagram.png') 29 | dirs = os.path.dirname(filename) 30 | if not os.path.exists(dirs): 31 | os.makedirs(dirs) 32 | plt.savefig(filename, bbox_inches='tight', pad_inches=0) 33 | -------------------------------------------------------------------------------- /src/microstructpy/examples/two_phase_3D.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 6 | lognorm 7 | 1 8 | 0.95 9 | 10 | sphere 11 | Phase 1 12 | 13 | 14 | 15 | 3 16 | 17 | lognorm 18 | 0.5 19 | 1.01 20 | 21 | Phase 2 22 | 23 | 24 | 25 | cube 26 | 7 27 | (0, 0, 0) 28 | 29 | 30 | 31 | two_phase_3D 32 | True 33 | 34 | 35 | 0.2 36 | 37 | 38 | 0.2 39 | 40 | 41 | 0.2 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/microstructpy/examples/uniform_seeding.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import os 4 | 5 | import matplotlib as mpl 6 | import numpy as np 7 | from matplotlib import pyplot as plt 8 | from scipy.spatial import distance 9 | 10 | import microstructpy as msp 11 | 12 | # Create domain 13 | domain = msp.geometry.Square(corner=(0, 0)) 14 | 15 | # Create list of seed points 16 | factory = msp.seeding.Seed.factory 17 | n = 200 18 | seeds = msp.seeding.SeedList([factory('circle', r=0.007) for i in range(n)]) 19 | 20 | # Position seeds according to Mitchell's Best Candidate Algorithm 21 | np.random.seed(0) 22 | 23 | lims = np.array(domain.limits) * (1 - 1e-5) 24 | centers = np.zeros((n, 2)) 25 | 26 | for i in range(n): 27 | f = np.random.rand(i + 1, 2) 28 | pts = f * lims[:, 0] + (1 - f) * lims[:, 1] 29 | try: 30 | min_dists = distance.cdist(pts, centers[:i]).min(axis=1) 31 | i_max = np.argmax(min_dists) 32 | except ValueError: # this is the case when i=0 33 | i_max = 0 34 | centers[i] = pts[i_max] 35 | seeds[i].position = centers[i] 36 | 37 | # Create Voronoi diagram 38 | pmesh = msp.meshing.PolyMesh.from_seeds(seeds, domain) 39 | 40 | # Set colors based on area 41 | areas = pmesh.volumes 42 | std_area = domain.area / n 43 | min_area = min(areas) 44 | max_area = max(areas) 45 | cell_colors = np.zeros((n, 3)) 46 | for i in range(n): 47 | if areas[i] < std_area: 48 | f_red = (areas[i] - min_area) / (std_area - min_area) 49 | f_green = (areas[i] - min_area) / (std_area - min_area) 50 | f_blue = 1 51 | else: 52 | f_red = 1 53 | f_green = (max_area - areas[i]) / (max_area - std_area) 54 | f_blue = (max_area - areas[i]) / (max_area - std_area) 55 | cell_colors[i] = (f_red, f_green, f_blue) 56 | 57 | # Create colorbar 58 | vs = (std_area - min_area) / (max_area - min_area) 59 | colors = [(0, (0, 0, 1)), (vs, (1, 1, 1)), (1, (1, 0, 0))] 60 | cmap = mpl.colors.LinearSegmentedColormap.from_list('area_cmap', colors) 61 | norm = mpl.colors.Normalize(vmin=min_area, vmax=max_area) 62 | sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm) 63 | sm.set_array([]) 64 | cb = plt.colorbar(sm, ticks=[min_area, std_area, max_area], ax=plt.gca(), 65 | orientation='horizontal', fraction=0.046, pad=0.08) 66 | cb.set_label('Cell Area') 67 | 68 | # Plot Voronoi diagram and seed points 69 | pmesh.plot(edgecolors='k', facecolors=cell_colors) 70 | seeds.plot(edgecolors='k', facecolors='none') 71 | 72 | plt.axis('square') 73 | plt.xlim(domain.limits[0]) 74 | plt.ylim(domain.limits[1]) 75 | 76 | # Save diagram 77 | file_dir = os.path.dirname(os.path.realpath(__file__)) 78 | filename = os.path.join(file_dir, 'uniform_seeding/voronoi_diagram.png') 79 | dirs = os.path.dirname(filename) 80 | if not os.path.exists(dirs): 81 | os.makedirs(dirs) 82 | plt.savefig(filename, bbox_inches='tight', pad_inches=0) 83 | -------------------------------------------------------------------------------- /src/microstructpy/geometry/__init__.py: -------------------------------------------------------------------------------- 1 | from microstructpy.geometry.box import Box 2 | from microstructpy.geometry.box import Cube 3 | from microstructpy.geometry.circle import Circle 4 | from microstructpy.geometry.ellipse import Ellipse 5 | from microstructpy.geometry.ellipsoid import Ellipsoid 6 | from microstructpy.geometry.rectangle import Rectangle 7 | from microstructpy.geometry.rectangle import Square 8 | from microstructpy.geometry.sphere import Sphere 9 | 10 | __all__ = ['Box', 'Cube', 'Circle', 'Ellipse', 'Ellipsoid', 11 | 'Rectangle', 'Square', 'Sphere'] 12 | 13 | 14 | def factory(name, **kwargs): 15 | """Factory method for geometries. 16 | 17 | This function returns a geometry based on a string containing the 18 | name of the geometry and keyword arguments defining the geometry. 19 | 20 | .. note:: 21 | 22 | The function call is ``factory(name, **kwargs)``. Sphinx autodocs 23 | has dropped the first parameter. 24 | 25 | Args: 26 | name (str): {'box' | 'cube' | 'ellipse' | 'ellipsoid' | 'circle' | 27 | 'rectangle' | 'square' | 'sphere'} Name of geometry. 28 | **kwargs (dict): Arguments defining the geometry. 29 | 30 | """ 31 | geom = name.strip().lower() 32 | if geom in ('box', 'rectangular prism', 'cuboid'): 33 | return Box(**kwargs) 34 | elif geom == 'cube': 35 | return Cube(**kwargs) 36 | elif geom == 'ellipse': 37 | return Ellipse(**kwargs) 38 | elif geom == 'ellipsoid': 39 | return Ellipsoid(**kwargs) 40 | elif geom == 'circle': 41 | return Circle(**kwargs) 42 | elif geom == 'rectangle': 43 | return Rectangle(**kwargs) 44 | elif geom == 'square': 45 | return Square(**kwargs) 46 | elif geom == 'sphere': 47 | return Sphere(**kwargs) 48 | else: 49 | e_str = 'Cannot recognize geometry name: ' + geom 50 | raise ValueError(e_str) 51 | -------------------------------------------------------------------------------- /src/microstructpy/geometry/box.py: -------------------------------------------------------------------------------- 1 | """Box 2 | 3 | This module contains the Box class. 4 | 5 | """ 6 | # --------------------------------------------------------------------------- # 7 | # # 8 | # Import Modules # 9 | # # 10 | # --------------------------------------------------------------------------- # 11 | 12 | from __future__ import division 13 | 14 | import numpy as np 15 | from matplotlib import pyplot as plt 16 | from mpl_toolkits.mplot3d import Axes3D 17 | from mpl_toolkits.mplot3d.art3d import Poly3DCollection 18 | 19 | from microstructpy.geometry.n_box import NBox 20 | 21 | __author__ = 'Kenneth (Kip) Hart' 22 | 23 | 24 | # --------------------------------------------------------------------------- # 25 | # # 26 | # Box Class # 27 | # # 28 | # --------------------------------------------------------------------------- # 29 | class Box(NBox): 30 | """Box 31 | 32 | This class contains a generic, 3D box. The position and dimensions of the 33 | box can be specified using any of the parameters below. 34 | 35 | Without any parameters, this is a unit cube centered on the origin. 36 | 37 | Args: 38 | side_lengths (list): *(optional)* Side lengths. 39 | center (list): *(optional)* Center of box. 40 | corner (list): *(optional)* Bottom-left corner. 41 | limits (list): *(optional)* Bounds of box. 42 | bounds (list): *(optional)* Alias for ``limits``. 43 | 44 | """ 45 | def __init__(self, **kwargs): 46 | NBox.__init__(self, **kwargs) 47 | 48 | try: 49 | self.center 50 | except AttributeError: 51 | self.center = [0, 0, 0] 52 | 53 | try: 54 | self.side_lengths 55 | except AttributeError: 56 | self.side_lengths = [1, 1, 1] 57 | 58 | # ----------------------------------------------------------------------- # 59 | # Representation Function # 60 | # ----------------------------------------------------------------------- # 61 | def __repr__(self): 62 | repr_str = 'Box(' 63 | repr_str += 'center=' + repr(tuple(self.center)) + ', ' 64 | repr_str += 'side_lengths=' + repr(tuple(self.side_lengths)) + ')' 65 | return repr_str 66 | 67 | # ----------------------------------------------------------------------- # 68 | # Number of Dimensions # 69 | # ----------------------------------------------------------------------- # 70 | @property 71 | def n_dim(self): 72 | """int: number of dimensions, 3""" 73 | return 3 74 | 75 | # ----------------------------------------------------------------------- # 76 | # Volume # 77 | # ----------------------------------------------------------------------- # 78 | @property 79 | def volume(self): 80 | """float: volume of box, :math:`V=l_1 l_2 l_3`""" 81 | return self.n_vol 82 | 83 | # ----------------------------------------------------------------------- # 84 | # Plot Function # 85 | # ----------------------------------------------------------------------- # 86 | def plot(self, **kwargs): 87 | """Plot the box. 88 | 89 | This function adds an 90 | :class:`mpl_toolkits.mplot3d.art3d.Poly3DCollection` to the current 91 | axes. The keyword arguments are passed through to the Poly3DCollection. 92 | 93 | Args: 94 | **kwargs (dict): Keyword arguments for Poly3DCollection. 95 | 96 | """ # NOQA: E501 97 | if plt.gcf().axes: 98 | ax = plt.gca() 99 | else: 100 | ax = plt.gcf().add_subplot(projection=Axes3D.name) 101 | 102 | xlim, ylim, zlim = self.limits 103 | 104 | inds = [(0, 0), (0, 1), (1, 1), (1, 0)] 105 | 106 | # x faces 107 | f1 = np.array([(xlim[0], ylim[i], zlim[j]) for i, j in inds]) 108 | f2 = np.array([(xlim[1], ylim[i], zlim[j]) for i, j in inds]) 109 | 110 | # y faces 111 | f3 = np.array([(xlim[i], ylim[0], zlim[j]) for i, j in inds]) 112 | f4 = np.array([(xlim[i], ylim[1], zlim[j]) for i, j in inds]) 113 | 114 | # z faces 115 | f5 = np.array([(xlim[i], ylim[j], zlim[0]) for i, j in inds]) 116 | f6 = np.array([(xlim[i], ylim[j], zlim[1]) for i, j in inds]) 117 | 118 | # plot 119 | xy = [f1, f2, f3, f4, f5, f6] 120 | pc = Poly3DCollection(xy, **kwargs) 121 | ax.add_collection(pc) 122 | 123 | 124 | # --------------------------------------------------------------------------- # 125 | # # 126 | # Cube Class # 127 | # # 128 | # --------------------------------------------------------------------------- # 129 | class Cube(Box): 130 | """A cube. 131 | 132 | This class contains a generic, 3D cube. It is derived from the 133 | :class:`.Box` and contains the ``side_length`` property, rather than 134 | multiple side lengths. 135 | 136 | Without any parameters, this is a unit cube centered on the origin. 137 | 138 | Args: 139 | side_length (float): *(optional)* Side length. 140 | center (list, tuple, numpy.ndarray): *(optional)* Center of box. 141 | corner (list, tuple, numpy.ndarray): *(optional)* Bottom-left corner. 142 | """ 143 | def __init__(self, **kwargs): 144 | if 'side_length' in kwargs: 145 | kwargs['side_lengths'] = 3 * [kwargs['side_length']] 146 | 147 | Box.__init__(self, **kwargs) 148 | 149 | # ----------------------------------------------------------------------- # 150 | # Side Length Property # 151 | # ----------------------------------------------------------------------- # 152 | @property 153 | def side_length(self): 154 | """float: length of the side of the cube.""" 155 | return self.side_lengths[0] 156 | -------------------------------------------------------------------------------- /src/microstructpy/meshing/__init__.py: -------------------------------------------------------------------------------- 1 | from microstructpy.meshing.polymesh import PolyMesh 2 | from microstructpy.meshing.trimesh import RasterMesh 3 | from microstructpy.meshing.trimesh import TriMesh 4 | 5 | __all__ = ['PolyMesh', 'RasterMesh', 'TriMesh'] 6 | -------------------------------------------------------------------------------- /src/microstructpy/seeding/__init__.py: -------------------------------------------------------------------------------- 1 | from microstructpy.seeding.seed import Seed 2 | from microstructpy.seeding.seedlist import SeedList 3 | 4 | __all__ = ['Seed', 'SeedList'] 5 | -------------------------------------------------------------------------------- /tests/cli/test_includes.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import xmltodict 4 | 5 | from microstructpy import cli 6 | 7 | 8 | def test_equivalence(): 9 | print(os.getcwd()) 10 | files_dir = os.path.join(os.path.dirname(__file__), 'test_includes_files') 11 | with open(os.path.join(files_dir, 'expected_input.xml'), 'r') as file: 12 | expected = xmltodict.parse(file.read()) 13 | 14 | for fname in ['input.xml', 'different_dir/input.xml']: 15 | actual = cli.input2dict(os.path.join(files_dir, fname)) 16 | assert expected == actual 17 | -------------------------------------------------------------------------------- /tests/cli/test_includes_files/different_dir/input.xml: -------------------------------------------------------------------------------- 1 | 2 | ../materials.xml 3 | 4 | square 5 | 20 6 | 7 | 8 | True 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/cli/test_includes_files/expected_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fine 1 4 | circle 5 | 0.1 6 | 7 | 8 | 9 | Fine 2 10 | circle 11 | 0.1 12 | 13 | 14 | 15 | Coarse 16 | circle 17 | 0.3 18 | 19 | 20 | square 21 | 20 22 | 23 | 24 | True 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/cli/test_includes_files/fine_grained.xml: -------------------------------------------------------------------------------- 1 | 2 | circle 3 | 0.1 4 | 5 | -------------------------------------------------------------------------------- /tests/cli/test_includes_files/input.xml: -------------------------------------------------------------------------------- 1 | 2 | materials.xml 3 | 4 | square 5 | 20 6 | 7 | 8 | True 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/cli/test_includes_files/materials.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fine 1 4 | fine_grained.xml 5 | 6 | 7 | 8 | Fine 2 9 | fine_grained.xml 10 | 11 | 12 | 13 | Coarse 14 | circle 15 | 0.3 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/geometry/test_n_sphere.py: -------------------------------------------------------------------------------- 1 | import microstructpy as msp 2 | 3 | c1 = msp.geometry.n_sphere.NSphere(r=1, center=(0, 4, -1)) 4 | c2 = msp.geometry.n_sphere.NSphere(r=2, center=(3, 1)) 5 | c3 = msp.geometry.n_sphere.NSphere(d=4, center=(3, 1)) 6 | 7 | 8 | def test_eq(): 9 | assert c1 == c1 10 | assert not c2 == c1 11 | 12 | assert c2 == c3 13 | -------------------------------------------------------------------------------- /tests/meshing/test_polymesh.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import os 4 | import sys 5 | 6 | import numpy as np 7 | 8 | from microstructpy import geometry 9 | from microstructpy.meshing.polymesh import PolyMesh 10 | from microstructpy.meshing.polymesh import kp_loop 11 | from microstructpy.seeding import Seed 12 | from microstructpy.seeding import SeedList 13 | 14 | ''' 15 | Test Mesh 16 | 17 | D ------- C 18 | | | \ 19 | | | \ 20 | | | \ 21 | A ------- B -- E 22 | 23 | A = (0, 0) 24 | B = (1, 0) 25 | C = (1, 1) 26 | D = (0, 1) 27 | E = (1.5, 0) 28 | 29 | Facets: 30 | 0 A B 31 | 1 B C 32 | 2 C D 33 | 3 D A 34 | 4 B E 35 | 5 E C 36 | 37 | Regions: 38 | 0, 1, 2, 3 39 | 4, 5, 1 40 | 41 | Seed Numbers: 42 | 0 43 | 1 44 | 45 | Phase Numbers: 46 | 2 47 | 2 48 | 49 | ''' 50 | 51 | A = (0, 0) 52 | B = (1, 0) 53 | C = (1, 1) 54 | D = (0, 1) 55 | E = (1.5, 0) 56 | pts = [A, B, C, D, E] 57 | 58 | facets = [(0, 1), (1, 2), (2, 3), (3, 0), (1, 4), (4, 2)] 59 | 60 | regions = [(0, 1, 2, 3), (4, 5, 1)] 61 | 62 | seed_nums = [0, 1] 63 | phase_nums = [2, 2] 64 | 65 | pmesh = PolyMesh(pts, facets, regions, seed_nums, phase_nums) 66 | 67 | 68 | def test_eq(): 69 | assert pmesh == pmesh 70 | 71 | r2 = [(0, 3, 2, 1), (4, 1, 5)] 72 | pmesh2 = PolyMesh(pts, facets, r2, seed_nums, phase_nums) 73 | assert pmesh == pmesh2 74 | assert pmesh2 == pmesh 75 | 76 | assert pmesh != pts 77 | assert pmesh != PolyMesh(pts, facets, regions, seed_nums, phase_nums[:-1]) 78 | 79 | r3 = [(0, 3, 2), (4, 1, 5)] 80 | assert pmesh != PolyMesh(pts, facets, r3, seed_nums, phase_nums) 81 | 82 | pt2 = [(-1, 0), B, C, D, E] 83 | assert pmesh != PolyMesh(pt2, facets, regions, seed_nums, phase_nums) 84 | 85 | f2 = [(0, 1), (1, 3), (2, 3), (3, 0), (1, 4), (4, 2)] 86 | assert pmesh != PolyMesh(pts, f2, regions, seed_nums, phase_nums) 87 | 88 | pmesh3 = PolyMesh(pts, facets, regions) 89 | assert pmesh3 == pmesh3 90 | assert pmesh3 != pmesh 91 | 92 | 93 | def test_read_write(): 94 | pyver_str = sys.version.split(' ')[0] 95 | pyver_str = pyver_str.replace('.', '_') 96 | 97 | fname = 'tmp_' + pyver_str + '.txt' 98 | filepath = os.path.dirname(__file__) 99 | filename = filepath + '/' + fname 100 | 101 | pmesh.write(filename) 102 | rw_pmesh = PolyMesh.from_file(filename) 103 | os.remove(filename) 104 | assert pmesh == rw_pmesh 105 | 106 | alt_str = 'comments\n' + str(pmesh) 107 | with open(filename, 'w') as file: 108 | file.write(alt_str + '\n') 109 | rw_pmesh = PolyMesh.from_file(filename) 110 | os.remove(filename) 111 | assert pmesh == rw_pmesh 112 | 113 | 114 | def test_repr(): 115 | assert pmesh == eval(repr(pmesh)) 116 | 117 | pmesh2 = PolyMesh(pts, facets, regions) 118 | assert pmesh2 == eval(repr(pmesh2)) 119 | 120 | 121 | def test_from_seeds(): 122 | # d^2 = dx^2 + dy^2 - r^2 123 | # Let d = 3 124 | # And circle 1 has radius 4, 125 | # so dx = 5 126 | # And circle 2 has radius 2, 127 | # so dx = np.sqrt(13) 128 | # and in both cases, y= 0 129 | 130 | d = 3 131 | r1 = 4 132 | r2 = 2 133 | 134 | x1 = -np.sqrt(d * d + r1 * r1) 135 | x2 = np.sqrt(d * d + r2 * r2) 136 | 137 | p1 = 2 138 | p2 = 3 139 | 140 | x_len = 2 * 1.1 * max(-x1 + r1, x2 + r2) 141 | y_len = 2 * 1.1 * max(r1, r2) 142 | 143 | # create polymesh by hand 144 | A = (-0.5 * x_len, -0.5 * y_len) 145 | B = (0, -0.5 * y_len) 146 | C = (0.5 * x_len, -0.5 * y_len) 147 | D = (0.5 * x_len, 0.5 * y_len) 148 | E = (0, 0.5 * y_len) 149 | F = (-0.5 * x_len, 0.5 * y_len) 150 | 151 | pts = [A, B, C, D, E, F] 152 | facets = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0), (1, 4)] 153 | regions = [(0, 6, 4, 5), (1, 2, 3, 6)] 154 | poly_exp = PolyMesh(pts, facets, regions, [0, 1], [p1, p2]) 155 | 156 | # create polymesh from seeds and domain 157 | s1 = Seed.factory('circle', r=r1, center=(x1, 0), phase=p1) 158 | s2 = Seed.factory('circle', r=r2, center=(x2, 0), phase=p2) 159 | slist = SeedList([s1, s2]) 160 | 161 | lens = [x_len, y_len] 162 | dom = geometry.Rectangle(center=(0.0, 0.0), side_lengths=lens) 163 | 164 | poly_act = PolyMesh.from_seeds(slist, dom) 165 | assert poly_exp == poly_act 166 | 167 | 168 | def test_kp_loop(): 169 | p1 = [0, 1] 170 | p2 = [1, 2] 171 | p3 = [2, 0] 172 | pairs = [p1, p2, p3] 173 | loop = kp_loop(pairs) 174 | 175 | possible_pairs = [(0, 1, 2), 176 | (1, 2, 0), 177 | (2, 0, 1), 178 | (2, 1, 0), 179 | (1, 0, 2), 180 | (0, 2, 1)] 181 | assert tuple(loop) in possible_pairs 182 | -------------------------------------------------------------------------------- /tests/test_misc.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from microstructpy import _misc 4 | 5 | 6 | def test_from_str_int(): 7 | pairs = [('0', 0), 8 | ('1', 1), 9 | ('2', 2), 10 | ('-1', -1), 11 | ('-2', -2)] 12 | 13 | for int_str, int_exp in pairs: 14 | int_act = _misc.from_str(int_str) 15 | a_str = 'Expected ' + str(int_exp) + ' and got ' + str(int_act) 16 | assert int_exp == int_act, a_str 17 | 18 | 19 | def test_from_str_float(): 20 | pairs = [('1.234', 1.234), 21 | ('0.214', 0.214), 22 | ('-0.4', -0.4), 23 | ('1.2e5', 1.2e5)] 24 | 25 | for flt_str, flt_exp in pairs: 26 | flt_act = _misc.from_str(flt_str) 27 | a_str = 'Expected ' + str(flt_exp) + ' and got ' + str(flt_act) 28 | assert flt_exp == flt_act, a_str 29 | 30 | 31 | def test_from_str_bool(): 32 | pairs = [('True', True), 33 | ('False', False), 34 | ('true', True), 35 | ('false', False)] 36 | 37 | for bool_str, bool_exp in pairs: 38 | bool_act = _misc.from_str(bool_str) 39 | a_str = 'Expected ' + str(bool_exp) + ' and got ' + str(bool_act) 40 | a_str += ' for string ' + repr(bool_str) 41 | assert bool_exp == bool_act, a_str 42 | 43 | 44 | def test_from_str_list(): 45 | pairs = [('[0]', [0]), 46 | # ('[1, 0, a]', [1, 0, 'a']), 47 | ('[-2.3, true]', [-2.3, True])] 48 | 49 | for list_str, list_exp in pairs: 50 | list_act = _misc.from_str(list_str) 51 | assert len(list_exp) == len(list_act) 52 | for act_val, exp_val in zip(list_act, list_exp): 53 | assert act_val == exp_val 54 | 55 | 56 | ''' 57 | def test_from_str_list_of_lists(): 58 | lol_str = '[[1, 0, 0, True, False], [2, 4, a, -2.3]]' 59 | lol_exp = [[1, 0, 0, True, False], [2, 4, 'a', -2.3]] 60 | 61 | lol_act = _misc.from_str(lol_str) 62 | 63 | assert len(lol_exp) == len(lol_act) 64 | for list_exp, list_act in zip(lol_exp, lol_act): 65 | assert len(list_exp) == len(list_act) 66 | for val_exp, val_act in zip(list_exp, list_act): 67 | assert val_exp == val_act 68 | ''' 69 | 70 | 71 | def test_tangent_sphere_2D(): 72 | pts = np.array([(0, 0), (4, 0), (3, 3)]) 73 | rads = np.array([1, 0.5, 0.5]) 74 | 75 | simps = [None, np.array([[0, 1, 2]])] 76 | for simp in simps: 77 | x, y, rad = _misc.tangent_sphere(pts, rads, simplices=simp) 78 | cen = np.array([x, y]) 79 | 80 | rel_pos = pts - cen 81 | dist = np.sqrt(np.sum(rel_pos * rel_pos, axis=-1)) 82 | assert np.all(np.isclose(dist, rads + rad)) 83 | 84 | 85 | def test_tangent_sphere_ND(): 86 | n = 7 87 | np.random.seed(0) 88 | pts = np.random.rand(n + 1, n) 89 | nsphere = _misc.tangent_sphere(pts) 90 | cen = nsphere[:-1] 91 | rad = nsphere[-1] 92 | 93 | rel_pos = pts - cen 94 | dist = np.sqrt(np.sum(rel_pos * rel_pos, axis=-1)) 95 | assert np.all(np.isclose(dist, rad)) 96 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # tox (https://tox.readthedocs.io/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = clean, 8 | check, 9 | py36, 10 | py37 11 | docs 12 | 13 | [testenv] 14 | deps = pytest 15 | -rrequirements.txt 16 | setenv = MPLBACKEND = agg 17 | commands = 18 | pytest 19 | 20 | [testenv:cov] 21 | deps = pytest 22 | pytest-cov 23 | basepython = python3.6 24 | usedevelop = True 25 | setenv = MPLBACKEND = agg 26 | commands = 27 | pytest --cov src/microstructpy --cov-report=html --cov-branch 28 | 29 | [testenv:check] 30 | deps = docutils 31 | check-manifest 32 | flake8 33 | readme-renderer 34 | pygments 35 | isort 36 | twine 37 | readme_renderer[md] 38 | skip_install = true 39 | commands = python setup.py sdist check --strict --metadata 40 | check-manifest {toxinidir} 41 | flake8 src tests setup.py --exclude=__init__.py 42 | isort --verbose --check-only --diff src tests setup.py 43 | twine check dist/* 44 | 45 | [testenv:docs] 46 | deps = -rdocs/requirements.txt 47 | basepython = python3.6 48 | commands = sphinx-build docs/source/ docs/build/ 49 | 50 | 51 | [testenv:docs-dev] 52 | deps = -rdocs/requirements.txt 53 | basepython = python3.6 54 | commands = sphinx-build -Wn docs/source/ docs/build/ 55 | sphinx-build -b latex docs/source/ docs/build-pdf/ 56 | sphinx-build -b epub docs/source/ docs/build-epub/ 57 | 58 | [testenv:clean] 59 | deps = coverage 60 | skip_install = true 61 | commands = coverage erase 62 | --------------------------------------------------------------------------------