diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4dda091cc..d1e309bd2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: arch: [x86_64] - cw_build: ["cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*"] + cw_build: ["cp310-*", "cp311-*", "cp312-*", "cp313-*", "cp314-*"] steps: - uses: actions/checkout@v4 @@ -35,7 +35,7 @@ jobs: strategy: matrix: arch: [arm64] - cw_build: ["cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*"] + cw_build: ["cp310-*", "cp311-*", "cp312-*", "cp313-*", "cp314-*"] steps: - uses: actions/checkout@v4 @@ -60,7 +60,7 @@ jobs: strategy: matrix: arch: [x86_64] - cw_build: ["cp39*many*", "cp310*many*", "cp311*many*", "cp312*many*", "cp313*many*"] + cw_build: ["cp310*many*", "cp311*many*", "cp312*many*", "cp313*many*", "cp314*many*"] steps: - uses: actions/checkout@v4 @@ -85,7 +85,7 @@ jobs: strategy: matrix: arch: [AMD64] - cw_build: ["cp39-*", "cp310-*", "cp311-*", "cp312-*", "cp313-*"] + cw_build: ["cp310-*", "cp311-*", "cp312-*", "cp313-*", "cp314-*"] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/test_full.yml b/.github/workflows/test_full.yml index 21b502252..9f060ac53 100644 --- a/.github/workflows/test_full.yml +++ b/.github/workflows/test_full.yml @@ -18,7 +18,7 @@ jobs: SPLINEPY_GITHUB_ACTIONS_BUILD: True strategy: matrix: - python-version: [3.9, "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] os: [ubuntu-latest, macos-latest, windows-latest] steps: diff --git a/.gitmodules b/.gitmodules index 2d8a696ec..b5ddf721c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,10 +3,10 @@ url = git@github.com:pybind/pybind11.git [submodule "third_party/napf"] path = third_party/napf - url = git@github.com:tataratat/napf.git + url = git@github.com:isosuite/napf.git [submodule "third_party/BSplineLib"] path = third_party/BSplineLib - url = git@github.com:tataratat/BSplineLib.git + url = git@github.com:isosuite/BSplineLib.git [submodule "third_party/bezman"] path = third_party/bezman - url = git@github.com:tataratat/bezman.git + url = git@github.com:isosuite/bezman.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 388115dcd..0be11c149 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,8 @@ # use default options for ci ci: - autoupdate_schedule: "weekly" + autoupdate_branch: 'develop' + autoupdate_schedule: "monthly" submodules: false repos: @@ -24,24 +25,19 @@ repos: - id: check-yaml - id: debug-statements - id: end-of-file-fixer + exclude: '^tests/data/.*$' - id: mixed-line-ending - id: requirements-txt-fixer - id: trailing-whitespace -- repo: https://github.com/psf/black-pre-commit-mirror - rev: "26.3.1" - hooks: - - id: black - additional_dependencies: [tomli] - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.7 + rev: v0.15.16 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v22.1.1 + rev: v22.1.5 hooks: - id: clang-format types_or: [c, c++] diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..c182baa29 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,157 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.2.1] - 2026-06 + +### Added + +#### Swept Surface/Solid Generation +- New `splinepy.helpme.create.swept()` function for generating swept surfaces and solids by sweeping a cross-section along a trajectory spline ([examples/show_swept.py](examples/show_swept.py)) + - Supports curve (1D) and surface (2D) cross-sections to produce surfaces and solids respectively + - Cross-section orientation uses the projection-normal method (Siltanen & Woodward), following [The NURBS Book](https://link.springer.com/book/10.1007/978-3-642-59223-2), Piegl & Tiller, 2nd ed., Chapter 10.4 + - Supports closed trajectories with orientation correction (p. 483 of the NURBS book) + - `anchor` parameter controls which reference point of the cross-section is placed on the trajectory (`"parametric"`, `"control_box"`, `"geometry_box"`, or `"auto"`) + - `set_on_trajectory` parameter selects between placement at trajectory knot evaluation points or trajectory control points + - Optional `rotation_adaption` parameter for additional rotation of the cross-section around the trajectory tangent + - Added swept surface image to README and documentation + +#### IGA Assembly: `FieldIntegrator` and `Transformation` +- New `Transformation` class in `splinepy.helpme.integrate` encapsulating all quadrature and geometry transformation data needed for IGA discretization: + - Precomputes quadrature points, supports, Jacobians, Jacobian inverses, and Jacobian determinants per element + - Configurable quadrature orders +- New `FieldIntegrator` class in `splinepy.helpme.integrate` providing a high-level interface for PDE assembly: + - `assemble_matrix(function)` — assembles the global stiffness matrix + - `assemble_vector(function)` — assembles the global load vector + - `assemble_matrix_and_vector(function)` — assembles both simultaneously + - `L2_projection(function)` — computes the L2 projection of a function onto the spline space + - `compute_error(function, norm)` — computes the L2 or H1 error of the current solution + - `apply_homogeneous_dirichlet_boundary_conditions()` — enforces homogeneous Dirichlet BCs + - `apply_dirichlet_boundary_conditions(function)` — enforces inhomogeneous Dirichlet BCs from a given function + - `solve_linear_system()` — solves the assembled linear system using `scipy.sparse.linalg.spsolve` + - Uses sparse matrix assembly via `scipy.sparse.dok_matrix` + +#### Microstructure: Tile Parameter Handling and Sensitivities +- Extended `TileBase` with standardised class attributes and properties for parameter validation and introspection: + - `_parameter_bounds` / `parameter_bounds` — bounds for each tile parameter + - `_parameters_shape` / `parameters_shape` — shape of the parameter array + - `_default_parameter_value` / `default_parameter_value` — default values for tile parameters + - `_sensitivities_implemented` / `sensitivities_implemented` — flag indicating whether parameter sensitivities are implemented + - `_closure_directions` / `closure_directions` — list of supported closure directions + - `check_params(parameters)` — validates user-supplied parameters against bounds + - `_process_input(parameters, parameter_sensitivities)` — shared input-processing logic + - `_check_custom_parameter(value, param_name, bounds)` — validates individual parameters +- Derived tile classes now inherit common parameter-processing code from `TileBase`, reducing code duplication +- Changed `evaluation_points`, `para_dim`, and `dim` from class methods to instance properties for consistency +- Added `n_info_per_eval_point` property to `TileBase` +- Derivatives (parameter sensitivities) implemented or fixed for: + - `HollowOctagon` (with and without closure) + - `HollowOctagonExtrude` (added working closure, with derivatives) + - `Armadillo` + - `Chi` tile (fixed) + - `InverseCross3D` (restructured and fixed) + - `CubeVoid` (fixed) + - `EllipsVoid` (fixed) +- Fourth-order accurate finite-difference calculation for numerical verification of parameter sensitivities +- Sensitivities calculation now outputs evaluation points when it fails for easier debugging +- `Microstructure`: integers are now accepted as tile parameters alongside floats + +#### Multipatch +- Added `Multipatch.set_interface_orientations(interface_orientations)` setter for manually specifying interface orientations, enabling export of scalar-field splines where automatic Jacobian-based orientation computation is not possible + +#### Examples +- Added `examples/show_swept.py` demonstrating swept surface and solid construction +- Added `examples/iga/galerkin_laplace_problem_field_integrator.py` demonstrating Galerkin IGA for the Laplace problem using `FieldIntegrator` +- Added `examples/iga/collocation_stokes_problem_sparse.py` demonstrating IGA collocation for the Stokes problem using sparse matrices + +### Changed + +#### CI/CD and Python Support +- Dropped Python 3.9 support; added Python 3.14 support (updated workflows and `pyproject.toml` classifiers) +- Switched macOS CI runner from deprecated `macos-13` to `macos-15-intel` for continued x86_64 testing +- Fixed wheel naming convention issue in CI + +#### Microstructure +- Refactored all tile classes to use common functions from `TileBase`, reducing code duplication significantly +- `TileBase`: removed class methods in favour of instance/class properties for `dim`, `para_dim`, and `evaluation_points` +- `TileBase`: removed unneeded classmethods to simplify the class hierarchy +- `Microstructure`: replaced bare `assert` statements with proper `raise` expressions for parameter validation; improved error messages +- `Microstructure`: updated `zip` calls to use `strict=True` for stricter length checking (Python 3.10+) +- `Chi` tile is now excluded from macro-sensitivity tests (analytical sensitivities not yet finalized) +- `HollowOctagonExtrude` removed from the list of skipped test tiles + +#### Integration (`helpme/integrate.py`) +- Extracted `_default_quadrature_orders(spline)` helper function (previously inlined in `_get_quadrature_information`) +- Improved docstrings for `_get_integral_measure`, `_get_quadrature_information`, and related functions +- Corrected typos in docstrings (`paramtric` → `parametric`, `Determinante` → `Determinant`) + +#### Gismo IO (`splinepy/io/gismo.py`) +- Added helper functions for Gismo export +- Fixed patch range text format for multi-patch exports + +#### IRIT IO (`splinepy/io/irit.py`) +- Updated `zip` calls to use `strict=True` + +#### MFEM IO (`splinepy/io/mfem.py`) +- Fixed test data for Cartesian 2D and 3D meshes + +#### PR Template +- Updated pull request template to use `develop` as the default base branch + +### Fixed + +- **`helpme/create.py`**: Fixed error in rotation matrix calculation for e2 entries in swept surface generation; improved input validation and error messages throughout +- **`helpme/integrate.py`**: Fixed typos and improved docstrings +- **`microstructure/tiles`**: Fixed incorrect ordering in finite-difference sensitivity calculation; fixed derivative implementations for `Chi`, `CubeVoid`, `EllipsVoid`, `InverseCross3D`; fixed typo causing errors in `HollowOctagonExtrude` +- **`microstructure/microstructure.py`**: Fixed typo in class docstring (`facilitatae` → `facilitate`) +- **`multipatch.py`**: Added check to verify that Jacobians exist before computing interface orientations +- **`bspline.py`**: Allow any negative number in the knot vector (previously restricted), fixing issue [#476](https://github.com/isosuite/splinepy/pull/476) +- **`utils/data.py`**: Minor fixes +- **Various IO modules**: Fixed miscellaneous issues and improved error handling + +### Tests + +- Added `tests/test_microtiles.py` with comprehensive parametrised tests for all microtile classes: + - All tiles lie within the unit cube for default parameters + - Tiles with non-default parameters + - Closure tests + - Tile derivative (sensitivity) tests, including closure derivatives + - Macro-sensitivity tests (finite-difference verification) +- Added `tests/test_microstructure.py` with additional microstructure integration tests +- Added test for `Transformation` class in `tests/helpme/test_integrate.py` +- Extended `tests/helpme/test_create.py` with swept surface tests, including: + - Comparison of swept vs extruded surfaces + - Custom cross-section normal vector + - Anchor modes + - Derivative checks +- Fixed RNG seed, sensitivity step size, and number of random test points for reproducible results +- Updated various existing tests for compatibility with Python 3.10+ `zip(strict=True)` changes + +## [0.2.0] - 2025-06-01 + +- Physical function integration in `helpme/integrate.py` +- Gismo export helper functions +- Python 3.13 support +- Various bug fixes and CI/CD maintenance + +## [0.1.3] - 2024 + +- Proximity / nearest-point query updates + +## [0.1.2] - 2024 + +- MFEM 3D single-patch IO +- Padding initializer for splines + +## [0.1.1] - 2024 + +- Initial stable release series + +[0.2.1]: https://github.com/isosuite/splinepy/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/isosuite/splinepy/compare/v0.1.3...v0.2.0 +[0.1.3]: https://github.com/isosuite/splinepy/compare/v0.1.2...v0.1.3 +[0.1.2]: https://github.com/isosuite/splinepy/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/isosuite/splinepy/releases/tag/v0.1.1 diff --git a/README.md b/README.md index 4cf35bfcb..3e3f46ad6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ ![txt_logo](docs/source/_static/splinepy_name.png) **splinepy - Library for prototyping spline geometries of arbitrary dimensions and degrees, and IGA** -[![workflow](https://github.com/tataratat/splinepy/actions/workflows/main.yml/badge.svg)](https://github.com/tataratat/splinepy/actions) +[![workflow](https://github.com/isosuite/splinepy/actions/workflows/main.yml/badge.svg)](https://github.com/isosuite/splinepy/actions) [![PyPI version](https://badge.fury.io/py/splinepy.svg)](https://badge.fury.io/py/splinepy) -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/tataratat/try-splinepy/main) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/isosuite/try-splinepy/main) ![gallery](docs/source/_static/gallery.png) @@ -19,7 +19,7 @@ pip install splinepy You can install it directly from the source: ```bash -git clone git@github.com:tataratat/splinepy.git +git clone git@github.com:isosuite/splinepy.git cd splinepy git submodule update --init --recursive pip install -e . @@ -27,15 +27,15 @@ pip install -e . ## Documentation Here are links to related documentation for the library: -- [Documentation home](https://tataratat.github.io/splinepy) -- [Introduction to splines](https://tataratat.github.io/splinepy/spline_intro.html#introduction-to-splines) -- [Spline visualization](https://tataratat.github.io/splinepy/spline_intro.html#visualizing-splines) +- [Documentation home](https://isosuite.github.io/splinepy) +- [Introduction to splines](https://isosuite.github.io/splinepy/spline_intro.html#introduction-to-splines) +- [Spline visualization](https://isosuite.github.io/splinepy/spline_intro.html#visualizing-splines) ## Quick start ### 1. Create a spline -Here, we will create a [NURBS](https://tataratat.github.io/splinepy/_generated/splinepy.nurbs.NURBS.html#splinepy.nurbs.NURBS) for the following example. Alternatively, we can also create [Bezier](https://tataratat.github.io/splinepy/_generated/splinepy.bezier.Bezier.html#splinepy.bezier.Bezier), [RationalBezier](https://tataratat.github.io/splinepy/_generated/splinepy.rational_bezier.RationalBezier.html#splinepy.rational_bezier.RationalBezier), and [BSpline](https://tataratat.github.io/splinepy/_generated/splinepy.bspline.BSpline.html#splinepy.bspline.BSpline). +Here, we will create a [NURBS](https://isosuite.github.io/splinepy/_generated/splinepy.nurbs.NURBS.html#splinepy.nurbs.NURBS) for the following example. Alternatively, we can also create [Bezier](https://isosuite.github.io/splinepy/_generated/splinepy.bezier.Bezier.html#splinepy.bezier.Bezier), [RationalBezier](https://isosuite.github.io/splinepy/_generated/splinepy.rational_bezier.RationalBezier.html#splinepy.rational_bezier.RationalBezier), and [BSpline](https://isosuite.github.io/splinepy/_generated/splinepy.bspline.BSpline.html#splinepy.bspline.BSpline). ```python import splinepy @@ -73,9 +73,9 @@ nurbs.show() ### 2. Modifications All the splines can be modified. For example, by 1. directly accessing properties, -2. [elevating degrees](https://tataratat.github.io/splinepy/_generated/splinepy.spline.Spline.elevate_degrees.html#splinepy.spline.Spline.elevate_degrees), -3. [inserting knots](https://tataratat.github.io/splinepy/_generated/splinepy.bspline.BSplineBase.insert_knots.html#splinepy.bspline.BSplineBase.insert_knots), -4. [reducing degrees](https://tataratat.github.io/splinepy/_generated/splinepy.bspline.BSplineBase.reduce_degrees.html) and [removing knots](https://tataratat.github.io/splinepy/_generated/splinepy.bspline.BSplineBase.remove_knots.html) with a specified tolerance +2. [elevating degrees](https://isosuite.github.io/splinepy/_generated/splinepy.spline.Spline.elevate_degrees.html#splinepy.spline.Spline.elevate_degrees), +3. [inserting knots](https://isosuite.github.io/splinepy/_generated/splinepy.bspline.BSplineBase.insert_knots.html#splinepy.bspline.BSplineBase.insert_knots), +4. [reducing degrees](https://isosuite.github.io/splinepy/_generated/splinepy.bspline.BSplineBase.reduce_degrees.html) and [removing knots](https://isosuite.github.io/splinepy/_generated/splinepy.bspline.BSplineBase.remove_knots.html) with a specified tolerance *Note: currently {3, 4} are limited to BSpline families.* ```python @@ -130,7 +130,7 @@ p_basis0 = nurbs.basis(queries, nthreads=2) splinepy.settings.NTHREADS = 3 p_basis1 = nurbs.basis(queries) ``` -We also implemented [point inversion](https://tataratat.github.io/splinepy/_generated/splinepy.spline.Spline.proximities.html#splinepy-spline-spline-proximities) for splines. +We also implemented [point inversion](https://isosuite.github.io/splinepy/_generated/splinepy.spline.Spline.proximities.html#splinepy-spline-spline-proximities) for splines. ```python # see docs for options para_coordinates = nurbs.proximities(physical_coordinates) @@ -153,11 +153,11 @@ In cases, where you may have to compute derivatives at the inverted locations or ``` ### 4. Helper Modules -There's a list of helper modules under the namespace `splinepy.helpme` to boost prototyping efficiencies. Please check out the full list [here](https://tataratat.github.io/splinepy/_generated/splinepy.helpme.html)! +There's a list of helper modules under the namespace `splinepy.helpme` to boost prototyping efficiencies. Please check out the full list [here](https://isosuite.github.io/splinepy/_generated/splinepy.helpme.html)! Here are some highlights. #### 4.1 Create -[splinepy.helpme.create](https://tataratat.github.io/splinepy/_generated/splinepy.helpme.create.html#module-splinepy.helpme.create) module can help you create several primitive shapes and another spline based on the existing spline. +[splinepy.helpme.create](https://isosuite.github.io/splinepy/_generated/splinepy.helpme.create.html#module-splinepy.helpme.create) module can help you create several primitive shapes and another spline based on the existing spline. ```python # basic splines box = splinepy.helpme.create.box(1, 2, 3) # length per dim @@ -171,7 +171,7 @@ torus = splinepy.helpme.create.torus( splinepy.show(["box", box], ["disk", disk], ["torus", torus]) ``` ![create_basic](docs/source/_static/readme_create_basic.png) -For the latter, you can directly access such functions through [spline.create](https://tataratat.github.io/splinepy/_generated/splinepy.spline.Spline.create.html#splinepy.spline.Spline.create). +For the latter, you can directly access such functions through [spline.create](https://isosuite.github.io/splinepy/_generated/splinepy.spline.Spline.create.html#splinepy.spline.Spline.create). ```python # based on existing splines extruded = nurbs.create.extruded(extrusion_vector=[1, 2, 3]) @@ -182,9 +182,17 @@ revolved = nurbs.create.revolved( splinepy.show(["extruded", extruded], ["revolved", revolved]) ``` ![create_derived](docs/source/_static/readme_create_derived.png) +You can also create swept surfaces and solids along spline trajectories. +```python +swept = splinepy.helpme.create.swept( + cross_section=splinepy.helpme.create.circle(0.5).nurbs, + trajectory=trajectory, +) +``` +![show_swept](docs/source/_static/show_swept.png) ### 4.2 Extract -Using [splinepy.helpme.extract](https://tataratat.github.io/splinepy/_generated/splinepy.helpme.extract.html#module-splinepy.helpme.extract) module, you can extract meshes (as a [gustaf](https://tataratat.github.io/gustaf/index.html) object) +Using [splinepy.helpme.extract](https://isosuite.github.io/splinepy/_generated/splinepy.helpme.extract.html#module-splinepy.helpme.extract) module, you can extract meshes (as a [gustaf](https://isosuite.github.io/gustaf/index.html) object) ```python # extract meshes as gustaf objects control_mesh = nurbs.extract.control_mesh() @@ -198,7 +206,7 @@ splinepy.show( ) ``` ![extract_mesh](docs/source/_static/readme_extract_mesh.png) -or part of splines from an existing spline using [spline.extract](https://tataratat.github.io/splinepy/_generated/splinepy.spline.Spline.extract.html#splinepy.spline.Spline.extract). +or part of splines from an existing spline using [spline.extract](https://isosuite.github.io/splinepy/_generated/splinepy.spline.Spline.extract.html#splinepy.spline.Spline.extract). ```python # extract splines boundaries = nurbs.extract.boundaries() @@ -226,7 +234,7 @@ splinepy.show( ![extract_spline](docs/source/_static/readme_extract_spline.png) #### 4.3 Free-form deformation -Together with mesh types of [gustaf](https://tataratat.github.io/gustaf), we can perform [free-form deformation](https://tataratat.github.io/splinepy/_generated/splinepy.helpme.ffd.FFD.html) +Together with mesh types of [gustaf](https://isosuite.github.io/gustaf), we can perform [free-form deformation](https://isosuite.github.io/splinepy/_generated/splinepy.helpme.ffd.FFD.html) ```python import gustaf as gus @@ -248,7 +256,7 @@ deformed = ffd.mesh ![ffd](docs/source/_static/readme_ffd.png) #### 4.4 Fitting -You can [fit](https://tataratat.github.io/splinepy/_generated/splinepy.helpme.fit.html#module-splinepy.helpme.fit) your point data using splines. +You can [fit](https://isosuite.github.io/splinepy/_generated/splinepy.helpme.fit.html#module-splinepy.helpme.fit) your point data using splines. ```python data = [ [-0.955, 0.293], @@ -279,8 +287,8 @@ splinepy.show( #### 4.5 Mapper -[Mapper](https://tataratat.github.io/splinepy/_generated/splinepy.helpme.mapper.Mapper.html#splinepy.helpme.mapper.Mapper) class is a geometric mapping helper that brings expression and derivatives into the physical domain. -This is especially useful for trying collocation methods. Here, we show how you can create a left hand side matrix for a laplace problem - see [this example](https://github.com/tataratat/splinepy/blob/main/examples/iga/collocation_laplace_problem_sparse.py) for a full solution: +[Mapper](https://isosuite.github.io/splinepy/_generated/splinepy.helpme.mapper.Mapper.html#splinepy.helpme.mapper.Mapper) class is a geometric mapping helper that brings expression and derivatives into the physical domain. +This is especially useful for trying collocation methods. Here, we show how you can create a left hand side matrix for a laplace problem - see [this example](https://github.com/isosuite/splinepy/blob/main/examples/iga/collocation_laplace_problem_sparse.py) for a full solution:

```python @@ -305,14 +313,14 @@ laplacian_matrix = splinepy.utils.data.make_matrix( ``` ### 5. Microstructure -(Rational) Bezier splines in splinepy are capable of [composition](https://tataratat.github.io/splinepy/_generated/splinepy.bezier.BezierBase.compose.html#splinepy.bezier.BezierBase.compose), where you can place a spline (inner spline/function) into another spline (outer spline/function) in an exact fashion. +(Rational) Bezier splines in splinepy are capable of [composition](https://isosuite.github.io/splinepy/_generated/splinepy.bezier.BezierBase.compose.html#splinepy.bezier.BezierBase.compose), where you can place a spline (inner spline/function) into another spline (outer spline/function) in an exact fashion. We can systematically perform this to create certain shapes that consist of multiple inner splines. -The resulting shapes are called [microstructure](https://tataratat.github.io/splinepy/_generated/splinepy.microstructure.microstructure.Microstructure.html#splinepy.microstructure.microstructure.Microstructure)s and the inner spline that serves as a basis shape is called [tile](https://tataratat.github.io/splinepy/_generated/splinepy.microstructure.tiles.tile_base.TileBase.html#splinepy.microstructure.tiles.tile_base.TileBase). +The resulting shapes are called [microstructure](https://isosuite.github.io/splinepy/_generated/splinepy.microstructure.microstructure.Microstructure.html#splinepy.microstructure.microstructure.Microstructure)s and the inner spline that serves as a basis shape is called [tile](https://isosuite.github.io/splinepy/_generated/splinepy.microstructure.tiles.tile_base.TileBase.html#splinepy.microstructure.tiles.tile_base.TileBase). splinepy has several tiles that are ready to use. -Implementations of available tiles can be found [here](https://tataratat.github.io/splinepy/_generated/splinepy.microstructure.tiles.html). -However, it is easier to access them through [module functions](https://tataratat.github.io/splinepy/_generated/splinepy.microstructure.tiles.html): +Implementations of available tiles can be found [here](https://isosuite.github.io/splinepy/_generated/splinepy.microstructure.tiles.html). +However, it is easier to access them through [module functions](https://isosuite.github.io/splinepy/_generated/splinepy.microstructure.tiles.html): ```python splinepy.microstructure.tiles.show() ``` @@ -344,12 +352,12 @@ generated = microstructure.create() ![microstructures](docs/source/_static/readme_microstructure.png) -Please take a look at [this example](https://github.com/tataratat/splinepy/blob/main/examples/show_microstructures.py) for a broad overview of what microstructures can do! +Please take a look at [this example](https://github.com/isosuite/splinepy/blob/main/examples/show_microstructures.py) for a broad overview of what microstructures can do! ### 6. Multipatch -In practice, including [Microstructure](https://tataratat.github.io/splinepy/_generated/splinepy.microstructure.microstructure.Microstructure.html#splinepy.microstructure.microstructure.Microstructure)s, it is common to work with multiple patches. -For that, we provide a [Multipatch](https://tataratat.github.io/splinepy/_generated/splinepy.multipatch.Multipatch.html#splinepy.multipatch.Multipatch) class, equipped with various useful functionalities: +In practice, including [Microstructure](https://isosuite.github.io/splinepy/_generated/splinepy.microstructure.microstructure.Microstructure.html#splinepy.microstructure.microstructure.Microstructure)s, it is common to work with multiple patches. +For that, we provide a [Multipatch](https://isosuite.github.io/splinepy/_generated/splinepy.multipatch.Multipatch.html#splinepy.multipatch.Multipatch) class, equipped with various useful functionalities: - patch interface identification - boundary patch identification - boundary assignment with various options @@ -381,7 +389,7 @@ splinepy.io.gismo.export("microstructure.xml", generated) ![multipatch](docs/source/_static/readme_multipatch.png) ### 7. Input/output and vector graphics -splinepy supports various [IO formats](https://tataratat.github.io/splinepy/_generated/splinepy.io.html). +splinepy supports various [IO formats](https://isosuite.github.io/splinepy/_generated/splinepy.io.html). Most notably, [gismo](https://github.com/gismo/gismo) and [mfem](https://github.com/mfem/mfem) formats allow a seamless transition to analysis. In addition splinepy is also able to import and export the `iges` format. Specifically, `Type 126` (B-Spline curve) and `Type 128` (B-Spline surface). ```python # export @@ -392,16 +400,16 @@ quarter_circle = splinepy.io.mfem.load("quarter_circle.mesh") ``` -[svg format](https://tataratat.github.io/splinepy/_generated/splinepy.io.svg.export.html#splinepy.io.svg.export) enables true vector graphic export which preserves the smoothness of splines for publications/documentation. Try to zoom in! +[svg format](https://isosuite.github.io/splinepy/_generated/splinepy.io.svg.export.html#splinepy.io.svg.export) enables true vector graphic export which preserves the smoothness of splines for publications/documentation. Try to zoom in! ```python splinepy.io.svg.export("nurbs.svg", nurbs) ```

## Try online -You can also try splinepy online by clicking the [Binder](https://mybinder.org/v2/gh/tataratat/try-splinepy/main) badge above! +You can also try splinepy online by clicking the [Binder](https://mybinder.org/v2/gh/isosuite/try-splinepy/main) badge above! ## Contributing splinepy welcomes any form of contributions! -Feel free to write us an [issue](https://github.com/tataratat/splinepy/issues) or start a [discussion](https://github.com/tataratat/splinepy/discussions). -Contribution guidelines can be found [here](https://tataratat.github.io/splinepy/CONTRIBUTING.html). +Feel free to write us an [issue](https://github.com/isosuite/splinepy/issues) or start a [discussion](https://github.com/isosuite/splinepy/discussions). +Contribution guidelines can be found [here](https://isosuite.github.io/splinepy/CONTRIBUTING.html). diff --git a/docs/markdown/spline_plotting.md b/docs/markdown/spline_plotting.md index 513db8653..fc874055c 100644 --- a/docs/markdown/spline_plotting.md +++ b/docs/markdown/spline_plotting.md @@ -1,7 +1,7 @@ # Visualization One of the highlights of splinepy is that we can visualize splines. Most of the classes have their own `show()` function to visualize current state of each object. -Visualizations utilize mesh types and data structures of [gustaf](https://tataratat.github.io/gustaf/). +Visualizations utilize mesh types and data structures of [gustaf](https://isosuite.github.io/gustaf/). Then, actual rendering happens with [vedo](https://vedo.embl.es) - a powerful scientific analysis and visualization library - check them out for details! The following will give a brief introduction to spline visualization. @@ -63,7 +63,7 @@ nurbs.show() ![plotting_data](../source/_static/plotting_data.png) You can easily plot data on splines. Scalar data can be represented with a colormap and vector data can be represented with arrows. -You can take a look at [this example](https://github.com/tataratat/splinepy/blob/main/examples/show_spline_data.py) for detailed introduction. +You can take a look at [this example](https://github.com/isosuite/splinepy/blob/main/examples/show_spline_data.py) for detailed introduction. ```python # set data - we will plot self spline, which will plot coordinates nurbs.spline_data["coords"] = nurbs @@ -92,7 +92,7 @@ nurbs.show() # Nr. 3 ``` ## Examples -Take a look at the examples folder for more! +Take a look at the examples folder for more! ## Notebook plotting diff --git a/docs/source/_static/show_swept.png b/docs/source/_static/show_swept.png new file mode 100644 index 000000000..37c1db052 Binary files /dev/null and b/docs/source/_static/show_swept.png differ diff --git a/docs/source/handle_markdown.py b/docs/source/handle_markdown.py index 8bb227723..97bb569a6 100644 --- a/docs/source/handle_markdown.py +++ b/docs/source/handle_markdown.py @@ -16,7 +16,6 @@ import pathlib import re import warnings -from typing import List, Tuple # Path to this file. file_path = os.path.abspath(os.path.dirname(__file__)) @@ -38,7 +37,7 @@ def get_markdown_links(line: str) -> str: return possible or "" -def get_special_links(line: str) -> List[Tuple[str, str]]: +def get_special_links(line: str) -> list[tuple[str, str]]: """Get the special links from a string. Args: @@ -49,8 +48,7 @@ def get_special_links(line: str) -> List[Tuple[str, str]]: """ possible = [ - x[::-1] - for x in re.findall(r"", line) + x[::-1] for x in re.findall(r"", line) ] return possible or "" @@ -72,7 +70,7 @@ def get_github_path_from(link): _type_: _description_ """ return str(pathlib.Path(link).resolve()).replace( - repo_root, "https://raw.githubusercontent.com/tataratat/splinepy/main/" + repo_root, "https://raw.githubusercontent.com/isosuite/splinepy/main/" ) @@ -112,9 +110,7 @@ def get_common_parent(path1: str, path2: str) -> str: } -def process_file( - file: str, relative_links: bool = True, return_content: bool = False -): +def process_file(file: str, relative_links: bool = True, return_content: bool = False): """Process a markdown file. This function will process a markdown file. It will replace all relative @@ -149,9 +145,7 @@ def process_file( stacklevel=3, ) continue - if item[1].startswith( - ("http", "#") - ): # skip http links and anchors + if item[1].startswith(("http", "#")): # skip http links and anchors if "badge" in item[1]: continue line = line.replace( # noqa: PLW2901 @@ -172,17 +166,13 @@ def process_file( "See documentation for examples.", ) elif not relative_links: # generate links to github repo - new_path = get_github_path_from( - pathlib.Path(item[1]).resolve() - ) + new_path = get_github_path_from(pathlib.Path(item[1]).resolve()) else: # generate relative links common_sub_path, steps_back = get_common_parent( item[1], folder_to_save_to ) new_path = "../" * steps_back + str( - pathlib.Path(item[1]) - .resolve() - .relative_to(common_sub_path) + pathlib.Path(item[1]).resolve().relative_to(common_sub_path) ) line = line.replace(item[1], str(new_path)) # noqa: PLW2901 @@ -200,9 +190,7 @@ def process_file( if item[0].startswith("http"): # skip http links and anchors continue elif not relative_links: # generate links to github repo - new_path = get_github_path_from( - pathlib.Path(item[1]).resolve() - ) + new_path = get_github_path_from(pathlib.Path(item[1]).resolve()) else: # just link to static folder in docs new_path = "_static/" + str(pathlib.Path(item[1]).name) @@ -214,9 +202,7 @@ def process_file( if return_content: return content - with open( - os.path.join(folder_to_save_to, os.path.basename(file)), "w" - ) as f: + with open(os.path.join(folder_to_save_to, os.path.basename(file)), "w") as f: f.write(content) @@ -229,9 +215,7 @@ def prepare_file_for_PyPI(): Args: file (str): Path to the README file. """ - content = process_file( - "README.md", relative_links=False, return_content=True - ) + content = process_file("README.md", relative_links=False, return_content=True) with open("README.md", "w") as f: f.write(content) diff --git a/docs/source/index.rst b/docs/source/index.rst index 5941a0b6c..da6df798d 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,7 +16,7 @@ Table of Contents :caption: Library :maxdepth: 1 - Github + Github Contributing API Reference diff --git a/examples/iga/collocation_stokes_problem_sparse.py b/examples/iga/collocation_stokes_problem_sparse.py new file mode 100644 index 000000000..aa00655eb --- /dev/null +++ b/examples/iga/collocation_stokes_problem_sparse.py @@ -0,0 +1,445 @@ +r""" +This file implements a stokes test case based on the example stated in +"Finite Element Methods for Flow Problems", Donea and Huerta p.306 +chapter 6.8.1 example with analytical solution. +""" + +import matplotlib.pyplot as plt +import numpy as np +from scipy.sparse import bmat, linalg + +import splinepy as sp + +### +# Inputs +### + +# Define the dynamic viscosity +viscosity = 1 +mass_factor = 1e-1 # Weight the incompressibility equations in the LSQ solve +impose_pressure_bcs = False # Impose BCS for pressure + +# Define the Geometry (simple first order unit square) +geometry = sp.BSpline( + degrees=[1, 1], + control_points=[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]], + knot_vectors=[[0, 0, 1, 1], [0, 0, 1, 1]], +) + + +solution_field_pressure = sp.BSpline( + degrees=geometry.degrees, + control_points=np.ones((geometry.control_points.shape[0], 1)), + knot_vectors=geometry.knot_vectors, +) +solution_field_pressure.elevate_degrees([0, 1]) +# Refinement leads to quadratic spline with 10x10cps +solution_field_pressure.uniform_refine([1], 7) +solution_field_pressure.uniform_refine([0], 7) + +# Create Raviart-Thomas mixed style splines +solution_field_velocity_u = solution_field_pressure.copy() +# For div-conforming tensor-product spaces in 2D, the x-velocity has +# one higher degree in x, and the y-velocity has one higher degree in y. +solution_field_velocity_u.elevate_degrees([0]) +solution_field_velocity_v = solution_field_pressure.copy() +solution_field_velocity_v.elevate_degrees([1]) + +### +# Source Functions +### + + +def source_function_u(x_vec): + x = x_vec[:, 0] + y = x_vec[:, 1] + return ( + (12 - 24 * y) * x**4 + + (-24 + 48 * y) * x**3 + + (12 - 48 * y + 72 * y**2 - 48 * y**3) * x**2 + + (-2 + 24 * y - 72 * y**2 + 48 * y**3) * x + + (1 - 4 * y + 12 * y**2 - 8 * y**3) + ) + + +def source_function_v(x_vec): + x = x_vec[:, 0] + y = x_vec[:, 1] + return ( + (8 - 48 * y + 48 * y**2) * x**3 + + (-12 + 72 * y - 72 * y**2) * x**2 + + (4 - 24 * y + 48 * y**2 - 48 * y**3 + 24 * y**4) * x + + (-12 * y**2 + 24 * y**3 - 12 * y**4) + ) + + +def boundary_conditions_u(x_vec): + return np.zeros(x_vec.shape[0]) + + +def boundary_conditions_v(x_vec): + return np.zeros(x_vec.shape[0]) + + +def boundary_conditions_p(x_vec): + x = x_vec[:, 0] + return x * (1.0 - x) + + +### +# Computation of greville abscissae +### +greville_points_u = solution_field_velocity_u.greville_abscissae() +greville_points_v = solution_field_velocity_v.greville_abscissae() +greville_points_p = solution_field_pressure.greville_abscissae() + +### +# Computation of the matrices +### +mapper_u = solution_field_velocity_u.mapper(reference=geometry) +mapper_v = solution_field_velocity_v.mapper(reference=geometry) +mapper_p = solution_field_pressure.mapper(reference=geometry) + +# FIRST ROW ---------- +# A_u_tu = mu * delta(u) +A_u_tu = -viscosity * sp.utils.data.make_matrix( + *mapper_u.basis_laplacian_and_support(greville_points_u), + solution_field_velocity_u.cps.shape[0], + as_array=False, +) +# A_v_tu = 0 +A_v_tu = None +# A_p_tu = dpdx +gradient_p, support_p = mapper_p.basis_gradient_and_support(greville_points_u) +A_p_tu = sp.utils.data.make_matrix( + gradient_p[:, :, 0], + support_p, + solution_field_pressure.cps.shape[0], + as_array=False, +) +# RHS +rhs_u = source_function_u(geometry.evaluate(greville_points_u)) + +# SECOND ROW ---------- +# A_u_tv = 0 +A_u_tv = None +# A_v_tv = mu * delta(v) +A_v_tv = -viscosity * sp.utils.data.make_matrix( + *mapper_v.basis_laplacian_and_support(greville_points_v), + solution_field_velocity_v.cps.shape[0], + as_array=False, +) +# A_p_tv +gradient_p, support_p = mapper_p.basis_gradient_and_support(greville_points_v) +A_p_tv = sp.utils.data.make_matrix( + gradient_p[:, :, 1], + support_p, + solution_field_pressure.cps.shape[0], + as_array=False, +) +# RHS +rhs_v = source_function_v(geometry.evaluate(greville_points_v)) + +# THIRD ROW ---------- +# A_u_tp dudx +gradient_u, support_u = mapper_u.basis_gradient_and_support(greville_points_p) +A_u_tp = sp.utils.data.make_matrix( + mass_factor * gradient_u[:, :, 0], + support_u, + solution_field_velocity_u.cps.shape[0], + as_array=False, +) +# A_v_tp dvdx +gradient_v, support_v = mapper_v.basis_gradient_and_support(greville_points_p) +A_v_tp = sp.utils.data.make_matrix( + mass_factor * gradient_v[:, :, 1], + support_v, + solution_field_velocity_v.cps.shape[0], + as_array=False, +) +# A_p_tp = 0 +A_p_tp = None +# RHS +rhs_p = mass_factor * np.zeros(greville_points_p.shape[0]) + + +### +# Imposition of BCs +### +# For U-Velocity +boundary_evaluation_point_ids_u = np.concatenate( + ( + solution_field_velocity_u.multi_index[0, :], + solution_field_velocity_u.multi_index[-1, :], + solution_field_velocity_u.multi_index[1:-1, 0], + solution_field_velocity_u.multi_index[1:-1, -1], + ) +) +boundary_evaluation_points_u = greville_points_u[ + boundary_evaluation_point_ids_u +] + +# Create matrices and RHS +A_u_tpbcu = sp.utils.data.make_matrix( + *solution_field_velocity_u.basis_and_support(boundary_evaluation_points_u), + solution_field_velocity_u.cps.shape[0], + as_array=False, +) +A_v_tpbcu = None +A_p_tpbcu = None +rhs_tpbcu = boundary_conditions_u(boundary_evaluation_points_u) + +# For V-velocity +boundary_evaluation_point_ids_v = np.concatenate( + ( + solution_field_velocity_v.multi_index[0, :], + solution_field_velocity_v.multi_index[-1, :], + solution_field_velocity_v.multi_index[1:-1, 0], + solution_field_velocity_v.multi_index[1:-1, -1], + ) +) +boundary_evaluation_points_v = greville_points_v[ + boundary_evaluation_point_ids_v +] + +# Create matrices and RHS +A_u_tpbcv = None +A_v_tpbcv = sp.utils.data.make_matrix( + *solution_field_velocity_v.basis_and_support(boundary_evaluation_points_v), + solution_field_velocity_v.cps.shape[0], + as_array=False, +) +A_p_tpbcv = None +rhs_tpbcv = boundary_conditions_v(boundary_evaluation_points_v) + +# For Pressure Field (redundant?) +if impose_pressure_bcs: + # boundary conditions only applied to the sides x=0 and x=1 + # To apply to all 4 sides see bc for velocity field + boundary_evaluation_point_ids_p = np.concatenate( + ( + solution_field_pressure.multi_index[0, :], + solution_field_pressure.multi_index[-1, :], + ) + ) + boundary_evaluation_points_p = greville_points_p[ + boundary_evaluation_point_ids_p + ] + + # Create matrices and RHS + A_u_tpbcp = None + A_v_tpbcp = None + A_p_tpbcp = sp.utils.data.make_matrix( + *solution_field_pressure.basis_and_support( + boundary_evaluation_points_p + ), + solution_field_pressure.cps.shape[0], + as_array=False, + ) + rhs_tpbcp = boundary_conditions_p(boundary_evaluation_points_p) + + +### +# Solve linear system +### +rhs_all = np.concatenate([rhs_u, rhs_v, rhs_p, rhs_tpbcu, rhs_tpbcv]) +block_matrices = [ + [A_u_tu, A_v_tu, A_p_tu], + [A_u_tv, A_v_tv, A_p_tv], + [A_u_tp, A_v_tp, A_p_tp], + [A_u_tpbcu, A_v_tpbcu, A_p_tpbcu], + [A_u_tpbcv, A_v_tpbcv, A_p_tpbcv], +] + +# L2-imposition of boundary conditions for the pressure field (optional) +if impose_pressure_bcs: + rhs_all = np.concatenate([rhs_all, rhs_tpbcp]) + block_matrices.append([A_u_tpbcp, A_v_tpbcp, A_p_tpbcp]) + +matrix_all = bmat(block_matrices, format=A_u_tu.format) +solution_vector, istop, itn, r1norm = linalg.lsqr( + matrix_all, + rhs_all, + atol=1e-12, + btol=1e-12, + iter_lim=100 * matrix_all.shape[1], + # calc_var=True +)[:4] +print(f"LSQR istop={istop}, iterations={itn}, residual={r1norm:.3e}") + + +# Plot Matrix +plt.spy(matrix_all) +plt.show() + + +### +# Assign solution to original splines +### +solution_field_velocity_u.control_points = np.reshape( + solution_vector[: solution_field_velocity_u.cps.shape[0]], (-1, 1) +) +solution_field_velocity_v.control_points = np.reshape( + solution_vector[ + solution_field_velocity_u.cps.shape[0] : -( + solution_field_pressure.cps.shape[0] + ) + ], + (-1, 1), +) +solution_field_pressure.control_points = np.reshape( + solution_vector[-(solution_field_pressure.cps.shape[0]) :], (-1, 1) +) + + +### +# Plot and analysis +### + +# Analytical solutions + + +def analytical_solution_u(geometry, on): + x_vec = geometry.evaluate(on) + x = x_vec[:, 0] + y = x_vec[:, 1] + return (x**2) * (1.0 - x) * (1.0 - x) * (2 * y - 6 * y**2 + 4 * y**3) + + +def error_u(solution_u): + def _error_u(data, on): + sol = solution_u.evaluate(on) + ansol = analytical_solution_u(data, on) + return np.abs(sol.flat - ansol) + + return _error_u + + +def analytical_solution_v(geometry, on): + x_vec = geometry.evaluate(on) + x = x_vec[:, 0] + y = x_vec[:, 1] + return -(y**2) * (1.0 - y) * (1.0 - y) * (2 * x - 6 * x**2 + 4 * x**3) + + +def error_v(solution_v): + def _error_v(data, on): + sol = solution_v.evaluate(on) + ansol = analytical_solution_v(data, on) + return np.abs(sol.flat - ansol) + + return _error_v + + +def analytical_solution_p(geometry, on): + x_vec = geometry.evaluate(on) + x = x_vec[:, 0] + return x * (1 - x) + + +# if we do not impose pressure BCs, we need to align the computed pressure +# field with the analytical solution because the pressure field is only +# determined up to a constant +pressure_error_offset = 0.0 +if not impose_pressure_bcs: + # we choose the offset as the minimum value of the computed pressure field + # such that the computed pressure field is (theoretically) non-negative + pressure_error_offset = solution_field_pressure.evaluate( + greville_points_p + ).min() + + +def error_p(solution_p): + def _error_p(data, on): + sol = solution_p.evaluate(on) + corr_sol = sol.flatten() - pressure_error_offset + ansol = analytical_solution_p(data, on) + return np.abs(corr_sol - ansol) + + return _error_p + + +# Plot geometry and fields +geometry.spline_data["field"] = solution_field_pressure +geometry.show_options["data"] = "field" +geometry.show_options["cmap"] = "jet" +geometry.show_options["lighting"] = "off" +geometry.show_options["scalarbar"] = True +solution_p = geometry.copy() +geometry.spline_data["field"] = solution_field_velocity_v +solution_v = geometry.copy() +geometry.spline_data["field"] = solution_field_velocity_u +solution_u = geometry.copy() +geometry.spline_data["field"] = sp.SplineDataAdaptor( + geometry, function=error_u(solution_field_velocity_u) +) +error_field_u = geometry.copy() +geometry.spline_data["field"] = sp.SplineDataAdaptor( + geometry, function=error_v(solution_field_velocity_v) +) +error_field_v = geometry.copy() +geometry.spline_data["field"] = sp.SplineDataAdaptor( + geometry, function=error_p(solution_field_pressure) +) +error_field_p = geometry.copy() + + +### +# Loss function u +### + +# Check Loss function +sample_points = greville_points_p +mapper_u = solution_field_velocity_u.mapper(reference=geometry) +mapper_v = solution_field_velocity_v.mapper(reference=geometry) +mapper_p = solution_field_pressure.mapper(reference=geometry) + + +def loss_function_u(data, on): + return np.abs( + -viscosity * mapper_u.laplacian(on).ravel() + + mapper_p.gradient(on)[:, 0, 0] + - source_function_u(data.evaluate(on)) + ) + + +def loss_function_v(data, on): + return np.abs( + -viscosity * mapper_v.laplacian(on).ravel() + + mapper_p.gradient(on)[:, 0, 1] + - source_function_v(data.evaluate(on)) + ) + + +def loss_function_p(_data, on): + return np.abs( + mass_factor + * (mapper_u.gradient(on)[:, 0, 0] + mapper_v.gradient(on)[:, 0, 1]) + ) + + +geometry.spline_data["field"] = sp.SplineDataAdaptor( + geometry, function=loss_function_u +) +loss_u = geometry.copy() +geometry.spline_data["field"] = sp.SplineDataAdaptor( + geometry, function=loss_function_v +) +loss_v = geometry.copy() +geometry.spline_data["field"] = sp.SplineDataAdaptor( + geometry, function=loss_function_p +) +loss_p = geometry.copy() +sp.show( + ["U-Velocity", solution_u], + ["V-velocity", solution_v], + ["Pressure", solution_p], + ["|U - U_exp|", error_field_u], + ["|V - V_exp|", error_field_v], + ["|P - P_exp|", error_field_p], + ["|R(u)|", loss_u], + ["|R(v)|", loss_v], + ["|R(p)|", loss_p], + knots=True, + control_points=False, +) diff --git a/examples/iga/galerkin_laplace_problem_field_integrator.py b/examples/iga/galerkin_laplace_problem_field_integrator.py new file mode 100644 index 000000000..a9ac952d8 --- /dev/null +++ b/examples/iga/galerkin_laplace_problem_field_integrator.py @@ -0,0 +1,177 @@ +r""" +This is a showcase of splinepy's FieldIntegrator class. It solves the Poisson +equation in the form +.. math:: + -\Delta u = f +with source term f=1. + +First, homogeneous Dirichlet boundary conditions are applied. Then, a mix of homogeneous +and inhomogeneous Dirichlet boundary conditions are applied on 3 of 4 boundaries. A +zero Neumann boundary condition is implicitly applied on the fourth boundary. +""" + +import numpy as np + +import splinepy as sp + +# Number of refinements for the solution field +n_refine = 15 + + +def prepare_geometry_and_solution_field(): + """ + Creates single patch geometry and solution field with h- and p-refinement + + Returns + ----------------- + geometry: splinepy.BSpline + The geometry object + solution_field: splinepy.BSpline + The solution field with refinements. Its control points serve as the degree of + freedoms (DoFs) of the solution. It is initialized to a vector of ones. + """ + # Define the Geometry + geometry = sp.BSpline( + degrees=[2, 2], + control_points=[ + [0.0, 0.0], + [1.0, 0.5], + [2.0, 0.2], + [0.5, 1.5], + [1.0, 1.5], + [1.5, 1.5], + [0.0, 3.0], + [1.0, 2.5], + [2.0, 3.0], + ], + knot_vectors=[[0, 0, 0, 1, 1, 1], [0, 0, 0, 1, 1, 1]], + ) + + # Define the solution field + solution_field = geometry.copy() + # Initialize solution vector + solution_field.control_points = np.ones( + (geometry.control_points.shape[0], 1) + ) + + # Apply p-refinement + solution_field.elevate_degrees([0, 1, 0, 1]) + # Apply uniform h-refinement + new_knots = np.linspace(1 / n_refine, 1, n_refine, endpoint=False) + solution_field.insert_knots(0, new_knots) + solution_field.insert_knots(1, new_knots) + + return geometry, solution_field + + +def poisson_lhs(mapper, quad_points, quad_weights, jacobian_det): + """ + Assemble the system matrix of the Poisson equation. + + Parameters + ----------- + mapper: splinepy.helpme.mapper.Mapper + Mapper of solution field + quad_points: np.ndarray + Quadrature points + quad_weights: np.ndarray + Quadrature weights + jacobian_det: np.ndarray + Determinant of Jacobian, evaluated at quadrature points + + Returns + ----------- + element_matrix: np.ndarray + Element matrix + """ + bf_gradient, _ = mapper.basis_gradient_and_support(quad_points) + element_matrix = np.einsum( + "qid,qjd,q,q->ij", + bf_gradient, + bf_gradient, + quad_weights, + jacobian_det, + optimize=True, + ) + return element_matrix.ravel() + + +def poisson_rhs(mapper, quad_points, quad_weights, jacobian_det): + """ + Assemble the rhs of the Poisson equation with f=1 + + Parameters + ----------- + mapper: splinepy.helpme.mapper.Mapper + Mapper of solution field + quad_points: np.ndarray + Quadrature points + quad_weights: np.ndarray + Quadrature weights + jacobian_det: np.ndarray + Determinant of Jacobian, evaluated at quadrature points + + Returns + ----------- + element_vector: np.ndarray + Element vector + """ + bf = mapper._field_reference.basis(quad_points) + element_vector = np.einsum( + "qj,q,q->j", bf, quad_weights, jacobian_det, optimize=True + ) + return element_vector + + +def show_solution(geometry, solution_field): + """Visualize the solution""" + geometry.spline_data["field"] = solution_field + geometry.show_options["data"] = "field" + geometry.show_options["cmap"] = "jet" + geometry.show_options["lighting"] = "off" + geometry.show_options["scalarbar"] = True + geometry.show(knots=True, control_points=False) + + +if __name__ == "__main__": + # Create geometry and solution field with h- and p-refinement + geometry, solution_field = prepare_geometry_and_solution_field() + + fi = sp.helpme.integrate.FieldIntegrator( + geometry=geometry, solution_field=solution_field + ) + + # Assemble the linear system for the Poisson equation + fi.assemble_matrix(poisson_lhs) + fi.assemble_vector(poisson_rhs) + + # Apply homogeneous Dirichlet boundary conditions + fi.apply_homogeneous_dirichlet_boundary_conditions() + # Solve linear system to obtain solution vector, which is stored as the + # solution_field's control points + fi.solve_linear_system() + # Plot geometry and field + show_solution(geometry, solution_field) + + # Function for inhomogeneous Dirichlet boundary conditions + def dirichlet_function(points): + """ + On the boundary apply: g(x,y) = x/4 + """ + return points[:, 0] / 4 + + # Assemble again to override previous boundary conditions + fi.assemble_matrix(poisson_lhs) + fi.assemble_vector(poisson_rhs) + + # Apply boundary conditions on 3 boundaries (west, south and north boundary) + fi.apply_homogeneous_dirichlet_boundary_conditions(west=True) + fi.apply_dirichlet_boundary_conditions( + dirichlet_function, south=True, north=True + ) + # For zero Neumann boundary conditions we can keep the matrix and rhs as they are + + # Solve linear system + fi.solve_linear_system() + # Plot resulting solution + show_solution(geometry, solution_field) diff --git a/examples/ipynb/notebook_showcase_k3d.ipynb b/examples/ipynb/notebook_showcase_k3d.ipynb index b576fe425..32e4a0f4f 100644 --- a/examples/ipynb/notebook_showcase_k3d.ipynb +++ b/examples/ipynb/notebook_showcase_k3d.ipynb @@ -192,9 +192,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Have you heard of spline composition? With this method, you can create spline based microstructures in exact fashion. This is one of the highlights of the `splinepy`. For more information, please take a look at splinepy's [docs](https://tataratat.github.io/splinepy/_generated/splinepy.bezier.BezierBase.compose.html#splinepy.bezier.BezierBase.compose)\n", + "Have you heard of spline composition? With this method, you can create spline based microstructures in exact fashion. This is one of the highlights of the `splinepy`. For more information, please take a look at splinepy's [docs](https://isosuite.github.io/splinepy/_generated/splinepy.bezier.BezierBase.compose.html#splinepy.bezier.BezierBase.compose)\n", "\n", - "Creating microstructures require two ingredients: outer spline (also known as deformation function, outer function, ...) and a microtile. For this example, we will use empty torus as outer spline and 2d cross as microtile (see other available ready-to-use microtiles [here](https://tataratat.github.io/splinepy/_generated/splinepy.microstructure.tiles.html))." + "Creating microstructures require two ingredients: outer spline (also known as deformation function, outer function, ...) and a microtile. For this example, we will use empty torus as outer spline and 2d cross as microtile (see other available ready-to-use microtiles [here](https://isosuite.github.io/splinepy/_generated/splinepy.microstructure.tiles.html))." ] }, { diff --git a/examples/show_microstructures.py b/examples/show_microstructures.py index 35d6d22b8..14d74dc04 100644 --- a/examples/show_microstructures.py +++ b/examples/show_microstructures.py @@ -346,13 +346,13 @@ def foo(x): generator.parametrization_function = foo inverse_microstructure = generator.create( - closing_face="z", seperator_distance=0.4, center_expansion=1.3 + closing_face="z", separator_distance=0.4, center_expansion=1.3 ) # Plot the results _, showables_inverse = generator.show( closing_face="z", - seperator_distance=0.4, + separator_distance=0.4, center_expansion=1.3, title="Parametrized Inverse Microstructure", control_points=False, @@ -365,7 +365,7 @@ def foo(x): # Corresponding Structure generator.microtile = splinepy.microstructure.tiles.get("Cross3D") microstructure = generator.create( - closing_face="z", seperator_distance=0.4, center_expansion=1.3 + closing_face="z", separator_distance=0.4, center_expansion=1.3 ) _, showables = generator.show( closing_face="z", diff --git a/examples/show_readme.py b/examples/show_readme.py index d7456e08a..ead7f06f0 100644 --- a/examples/show_readme.py +++ b/examples/show_readme.py @@ -97,7 +97,7 @@ para_queries = [] phys_queries = [] colors = ["red", "yellow", "blue"] -for q, pc, c in zip(queries, physical_coordinates, colors): +for q, pc, c in zip(queries, physical_coordinates, colors, strict=False): para_q, phys_q = gus.Vertices([q]), gus.Vertices([pc]) para_q.show_options["c"] = c para_q.show_options["r"] = 10 diff --git a/examples/show_swept.py b/examples/show_swept.py new file mode 100644 index 000000000..54eceed16 --- /dev/null +++ b/examples/show_swept.py @@ -0,0 +1,241 @@ +import gustaf as gus +import numpy as np + +import splinepy + +if __name__ == "__main__": + + ### TRAJECTORY ### + + # define a hook-trajectory + dict_trajectory = { + "degrees": [3], + "knot_vectors": [ + [0.0, 0.0, 0.0, 0.0, 0.2, 0.4, 0.6, 0.8, 0.9, 1.0, 1.0, 1.0, 1.0] + ], + "control_points": np.array( + [ + [0.5, 0], + [0.5, 2], + [1.0, 3], + [2.0, 4], + [2.15, 5], + [1.8, 5.9], + [1.0, 6.2], + [-0.25, 6], + [-0.5, 5], + ] + ), + } + + # create spline of trajectory dict + trajectory = splinepy.BSpline(**dict_trajectory) + + # refine trajectory by inserting knots and control points + trajectory.uniform_refine(0, 1) + + ### CROSS SECTIONS ### + + # 1. create a circular 1D-line-cross-section + cross_section_circle = splinepy.helpme.create.circle(0.5).nurbs + + # 2. create a circular 2D-surface-cross-section + cross_section_disk = splinepy.helpme.create.surface_circle(0.5).nurbs + + # 3. create a rectangular 2D-surface-cross-section + cross_section_plate = splinepy.helpme.create.box(1, 1).nurbs + + # 4. create a more complex 1D-line-cross-section + # use an asymmetric rational curve so control-box and geometry-box anchors + # visibly differ + cross_section_rectangle_line = splinepy.helpme.create.circle(0.55).nurbs + cross_section_rectangle_line.control_points[:, 0] *= 1.2 + cross_section_rectangle_line.control_points[:, 1] *= 0.8 + cross_section_rectangle_line.control_points[1] += np.array([0.9, -0.15]) + cross_section_rectangle_line.control_points[2] += np.array([0.45, 0.55]) + cross_section_rectangle_line.control_points[5] += np.array([-0.35, -0.45]) + cross_section_rectangle_line.weights[1] *= 0.25 + cross_section_rectangle_line.weights[2] *= 0.45 + cross_section_rectangle_line.weights[5] *= 0.5 + + # Define a custom normal vector for the cross-section when: + # a) The cross-section is not planar in the x-y plane, or + # b) Crooked sweeping is desired + cs_nv = np.array([1, 0, 1]) + + ### SWEEP ### + + # create swept surface + swept_surface_circle = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_circle, + cross_section_normal=None, + anchor="auto", + set_on_trajectory=False, + rotation_adaption=None, + ) + + # create crooked swept solid (circular nr. 1) + # the cross-section's normal vector is not default; crooked sweeping + swept_solid_disk_1 = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_disk, + cross_section_normal=cs_nv, + anchor="auto", + set_on_trajectory=False, + rotation_adaption=None, + ) + + # create swept solid (circular nr. 2) + # the cross-sections are set on the trajectory's control points (default) + swept_solid_disk_2 = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_disk, + cross_section_normal=None, + anchor="auto", + set_on_trajectory=False, + rotation_adaption=None, + ) + + # create swept solid (circular nr. 3) + # the cross-sections are set on the trajectory's evaluation points + swept_solid_disk_3 = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_disk, + cross_section_normal=None, + anchor="auto", + set_on_trajectory=True, + rotation_adaption=None, + ) + + # create swept solid (rectangular nr. 1) + swept_solid_plate_1 = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_plate, + cross_section_normal=None, + anchor="auto", + set_on_trajectory=False, + rotation_adaption=None, + ) + + # create swept solid (rectangular nr. 2) + # rotation adaption with 45 degrees + swept_solid_plate_2 = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_plate, + cross_section_normal=None, + anchor="auto", + set_on_trajectory=False, + rotation_adaption=45 * np.pi / 180, + ) + + # create swept surfaces to compare anchor placement strategies + swept_surface_rectangle_parametric = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_rectangle_line, + cross_section_normal=None, + anchor="parametric", + set_on_trajectory=False, + rotation_adaption=None, + ) + + swept_surface_rectangle_control_box = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_rectangle_line, + cross_section_normal=None, + anchor="control_box", + set_on_trajectory=False, + rotation_adaption=None, + ) + + swept_surface_rectangle_geometry_box = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_rectangle_line, + cross_section_normal=None, + anchor="geometry_box", + set_on_trajectory=False, + rotation_adaption=None, + ) + + swept_surface_rectangle_auto = splinepy.helpme.create.swept( + trajectory=trajectory, + cross_section=cross_section_rectangle_line, + cross_section_normal=None, + anchor="auto", + set_on_trajectory=False, + rotation_adaption=None, + ) + + ### VISUALIZATION ### + + # first window: swept surface + gus.show( + ["Trajectory", trajectory], + ["1D Cross Section", cross_section_circle], + ["Swept Surface", swept_surface_circle], + resolution=51, + control_mesh=False, + control_point_ids=False, + ) + + # adjust show options + swept_solid_disk_2.show_options["alpha"] = 0.3 + swept_solid_disk_3.show_options["alpha"] = 0.3 + trajectory.show_options["control_points"] = False + + # second window: swept solids (circular) + gus.show( + ["2D Cross Section", cross_section_disk], + ["Swept Solid - Crooked Sweeping", swept_solid_disk_1], + [ + "Swept Solid - Set on Control Points", + swept_solid_disk_2, + trajectory, + ], + [ + "Swept Solid - Set on Evaluation Points", + swept_solid_disk_3, + trajectory, + ], + resolution=51, + control_mesh=False, + control_point_ids=False, + ) + + # third window: swept solids (rectangular) + gus.show( + ["New Cross Section", cross_section_plate], + ["Swept Solid without Rotation", swept_solid_plate_1], + ["Swept Solid with 45° Rotation", swept_solid_plate_2], + resolution=51, + control_mesh=False, + control_point_ids=False, + ) + + # fourth window: anchor placement for line cross-sections + gus.show( + ["Rectangle Line Cross Section", cross_section_rectangle_line], + [ + "Anchor: parametric", + swept_surface_rectangle_parametric, + trajectory, + ], + [ + "Anchor: control_box", + swept_surface_rectangle_control_box, + trajectory, + ], + [ + "Anchor: geometry_box", + swept_surface_rectangle_geometry_box, + trajectory, + ], + [ + "Anchor: auto", + swept_surface_rectangle_auto, + trajectory, + ], + resolution=51, + control_mesh=False, + control_point_ids=False, + ) diff --git a/pyproject.toml b/pyproject.toml index c071f106b..9590bd36c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,16 +10,16 @@ authors = [ license = {file = "LICENSE"} description = "Library for prototyping spline geometries of arbitrary dimensions and degrees, and IGA" keywords = ["bezier", "rational bezier", "bspline", "nurbs", "multi patch"] -urls = {Homepage = "https://github.com/tataratat/splinepy"} +urls = {Homepage = "https://github.com/isosuite/splinepy"} classifiers=[ "Development Status :: 4 - Beta", "License :: OSI Approved :: MIT License", "Programming Language :: Python", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Natural Language :: English", "Topic :: Scientific/Engineering", ] @@ -91,7 +91,7 @@ lint.select = [ "A", # flake8-builtins ] lint.fixable = ["ALL"] -target-version = "py38" +target-version = "py310" lint.ignore = [ "PLR2004", # TODO! "PLR0912", # Too many branches @@ -113,9 +113,6 @@ lint.ignore = [ ignore-words-list = "connec,tye,ned" skip="./docs/source/_generated/**,./docs/build/*,./build/*,./third_party/*,./tests/data/*.svg,*.html,*.js" -[tool.black] -line-length = 79 -exclude = "third_party" [tool.blackdoc] line-length = 75 diff --git a/splinepy/_version.py b/splinepy/_version.py index d3ec452c3..3ced3581b 100644 --- a/splinepy/_version.py +++ b/splinepy/_version.py @@ -1 +1 @@ -__version__ = "0.2.0" +__version__ = "0.2.1" diff --git a/splinepy/bspline.py b/splinepy/bspline.py index e0d6f7320..237e3c255 100644 --- a/splinepy/bspline.py +++ b/splinepy/bspline.py @@ -108,8 +108,7 @@ def insert_knots(self, parametric_dimension, knots): if min(knots) < min(self.knot_vectors[parametric_dimension]): raise ValueError( - "One of the query knots not in valid knot range. " - "(Too small)" + "One of the query knots not in valid knot range. (Too small)" ) inserted = _splinepy_core.insert_knots( @@ -171,7 +170,7 @@ def determine_new_knots(kv_unique, n_knots): return new_knots # determine new knots for each para_dim and insert the knots - for para_dim, n_k in zip(para_dims, n_knots): + for para_dim, n_k in zip(para_dims, n_knots, strict=True): new_knots = determine_new_knots( # recompute unique to allow duplicating para_dims. kv_unique=self.unique_knots[para_dim], @@ -303,8 +302,7 @@ def remove_knots(self, parametric_dimension, knots, tolerance=None): if min(knots) < min(self.knot_vectors[parametric_dimension]): raise ValueError( - "One of the query knots not in valid knot range. " - "(Too small)" + "One of the query knots not in valid knot range. (Too small)" ) removed = _splinepy_core.remove_knots( @@ -319,7 +317,6 @@ def remove_knots(self, parametric_dimension, knots, tolerance=None): self._logd(f"Tried to remove {len(knots)} knot(s).") self._logd(f"Actually removed {sum(removed)} knot(s).") - return removed def normalize_knot_vectors(self): diff --git a/splinepy/helpme/check.py b/splinepy/helpme/check.py index 86f975bd0..4b6013d43 100644 --- a/splinepy/helpme/check.py +++ b/splinepy/helpme/check.py @@ -38,9 +38,9 @@ def valid_queries(spline, queries): error_query = _np.argmin(queries, axis=0)[error_dim] raise ValueError( f"Query request out of bounds in parametric dimension " - f"{error_dim}. Detected query {queries[error_query,:]} at " + f"{error_dim}. Detected query {queries[error_query, :]} at " f"positions {error_query}, which is out of bounds with " - f"minimum values {bounds[0,:]}." + f"minimum values {bounds[0, :]}." ) # Check maximum value @@ -50,9 +50,9 @@ def valid_queries(spline, queries): error_query = _np.argmax(queries, axis=0)[error_dim] raise ValueError( f"Query request out of bounds in parametric dimension " - f"{error_dim}. Detected query {queries[error_query,:]} at " + f"{error_dim}. Detected query {queries[error_query, :]} at " f"positions {error_query}, which is out of bounds with " - f"maximum values {bounds[1,:]}." + f"maximum values {bounds[1, :]}." ) return True @@ -78,7 +78,7 @@ def clamped_knot_vectors(spline, warning=True): if degrees is None or knot_vectors is None: return None - for d, kv in zip(degrees, knot_vectors): + for d, kv in zip(degrees, knot_vectors, strict=True): kv_arr = _np.asanyarray(kv) front = all(abs(kv_arr[: (d + 1)] - kv_arr[0]) < _settings.TOLERANCE) diff --git a/splinepy/helpme/create.py b/splinepy/helpme/create.py index 1e3d4ee3d..7ba756846 100644 --- a/splinepy/helpme/create.py +++ b/splinepy/helpme/create.py @@ -36,9 +36,7 @@ def embedded(spline, new_dimension): if spline.dim > new_dimension: spline_dict = spline.todict() - spline_dict["control_points"] = spline_dict["control_points"][ - :, :new_dimension - ] + spline_dict["control_points"] = spline_dict["control_points"][:, :new_dimension] return type(spline)(**spline_dict) elif spline.dim < new_dimension: spline_dict = spline.todict() @@ -105,10 +103,7 @@ def extruded(spline, extrusion_vector=None): ) ) else: - raise ValueError( - "Dimension Mismatch between extrusion extrusion vector " - "and spline." - ) + raise ValueError("Dimension Mismatch between extrusion vector and spline.") # Start Extrusion spline_dict = {} @@ -118,9 +113,7 @@ def extruded(spline, extrusion_vector=None): if spline.has_knot_vectors: spline_dict["knot_vectors"] = spline.knot_vectors + [[0, 0, 1, 1]] if spline.is_rational: - spline_dict["weights"] = _np.concatenate( - (spline.weights, spline.weights) - ) + spline_dict["weights"] = _np.concatenate((spline.weights, spline.weights)) return type(spline)(**spline_dict) @@ -161,9 +154,7 @@ def revolved( axis = _np.asarray(axis).ravel() # Check Axis dimension if spline.control_points.shape[1] > axis.shape[0]: - raise ValueError( - "Dimension Mismatch between extrusion axis and spline." - ) + raise ValueError("Dimension Mismatch between extrusion axis and spline.") elif spline.control_points.shape[1] < axis.shape[0]: _log.debug( "Control Point dimension is smaller than axis dimension," @@ -173,9 +164,7 @@ def revolved( cps = _np.hstack( ( spline.control_points, - _np.zeros( - (len(spline.control_points), expansion_dimension) - ), + _np.zeros((len(spline.control_points), expansion_dimension)), ) ) else: @@ -213,24 +202,20 @@ def revolved( center = _np.asarray(center).ravel() # Check Axis dimension if not (problem_dimension == center.shape[0]): - raise ValueError( - "Dimension Mismatch between axis and center of rotation." - ) + raise ValueError("Dimension Mismatch between axis and center of rotation.") cps -= center # The parametric dimension is independent of the revolution but the # rotation-matrix is only implemented for 2D and 3D problems if cps.shape[1] not in {2, 3}: raise NotImplementedError( - "Sorry," "revolutions only implemented for 2D and 3D splines" + "Sorry, revolutions only implemented for 2D and 3D splines" ) # Angle must be (0, pi) non including # Rotation is always performed in half steps PI = _np.pi - minimum_n_knot_spans = int( - _np.ceil((abs(angle) + abs(_settings.TOLERANCE)) / PI) - ) + minimum_n_knot_spans = int(_np.ceil((abs(angle) + abs(_settings.TOLERANCE)) / PI)) if n_knot_spans is None or (n_knot_spans < minimum_n_knot_spans): n_knot_spans = minimum_n_knot_spans @@ -276,9 +261,7 @@ def revolved( if spline.has_knot_vectors: kv = [0, 0, 0] [kv.extend([i + 1, i + 1]) for i in range(n_knot_spans - 1)] - spline_dict["knot_vectors"] = spline.knot_vectors + [ - kv + [n_knot_spans] * 3 - ] + spline_dict["knot_vectors"] = spline.knot_vectors + [kv + [n_knot_spans] * 3] if spline.is_rational: mid_weights = spline.weights * weight spline_dict["weights"] = spline.weights @@ -298,6 +281,401 @@ def revolved( return type(spline)(**spline_dict) +def swept( + cross_section, + trajectory, + cross_section_normal=None, + anchor="auto", + set_on_trajectory=False, + rotation_adaption=None, +): + """ + Sweeps a cross-section along a trajectory. The cross-section + receives rotation into the direction of the trajectory tangent + vector and is then placed either at the evaluation points of the + trajectory's knots or at the trajectory's control points. This + depends on the value of the set_on_trajectory parameter. It can + create both a surface or a solid, depending on the dimension of + the cross-section. + + The sweeping process has some limitations, since the cross-section + cannot be preserved exactly along the whole trajectory. + + This implementation follows the skinning-based swept surface + construction described in The NURBS Book, Piegl & Tiller, 2nd + edition, chapter 10.4, where cross-section instances are placed + along the trajectory and skinned afterwards. + + The cross-section orientation is determined using the projection + normal method of Siltanen and Woodward, as described in chapter + 10.4, Eq. (10.27). For closed trajectories, the orientation is + corrected as described on p. 483. + + Parameters + ---------- + cross_section : Spline + Cross-section to be swept. Requires parametric dimension 1 or 2 + trajectory : Spline + Trajectory along which the cross-section is swept. + Requires parametric dimension 1 + cross_section_normal : np.array + Normal vector of the cross-section + Default is [0, 0, 1] + anchor : str + Defines which reference point of the cross-section is placed on the + trajectory during sweeping. + ``"parametric"`` uses the point evaluated at the center of the + parametric bounds. + ``"control_box"`` uses the center of the control-point bounding box. + ``"geometry_box"`` uses the center of the bounding box of sampled + geometric points on the cross-section. + ``"auto"`` uses ``"geometry_box"`` for curve cross-sections and + ``"parametric"`` for surface cross-sections. + set_on_trajectory : bool + If True, the cross-section will be placed at the evaluation + points of the trajectory. If False, the cross-section will be + placed at the control points of the trajectory. + Default is False. + rotation_adaption : float + Angle in radians by which the cross-section is rotated around + the trajectory tangent vector. This is an additional rotation + if the user wants to adapt the cross-section rotation. + Example with rectangular cross-section: + . x + . x x x x x x x + . x x x x + . x x --> rotation around pi/4 --> x x + . x x x x + . x x x x x x x + . x + + Returns + ------- + swept_spline : Spline + Spline resulting from the sweep + + Examples + -------- + See ``examples/show_swept.py`` for example usages of ``swept()``. + """ + + from splinepy import NURBS as _NURBS + from splinepy import BSpline as _BSpline + from splinepy import Spline + + ### INPUT CHECKS ### + + if isinstance(cross_section, Spline) and isinstance(trajectory, Spline): + if not isinstance(cross_section, (_BSpline, _NURBS)): + raise TypeError("cross_section must be an instance of BSpline or NURBS") + if not isinstance(trajectory, (_BSpline, _NURBS)): + raise TypeError("trajectory must be an instance of BSpline or NURBS") + else: + raise TypeError("cross_section and trajectory must be instances of Spline") + if not trajectory.para_dim == 1: + raise ValueError("trajectory must have a parametric dimension of 1") + + if cross_section.para_dim > 2: + raise ValueError("cross_section must have a parametric dimension of at most 2") + + if not isinstance(set_on_trajectory, bool): + raise TypeError("set_on_trajectory must be a boolean") + + if rotation_adaption is not None: + try: + rotation_adaption = float(rotation_adaption) + except TypeError: + raise TypeError("rotation_adaption must be a number (float, int) or None") + + if cross_section_normal is None: + cross_section_normal = _np.array([0, 0, 1]) + # add debug message + _log.debug("No cross_section_normal given. Defaulting to [0, 0, 1].") + else: + try: + cross_section_normal = _np.asarray(cross_section_normal).ravel() + except (TypeError, ValueError): + raise TypeError("cross_section_normal must be array-like and a 3D vector") + if cross_section_normal.shape != (3,): + raise ValueError("cross_section_normal must be array-like and a 3D vector") + + if not isinstance(anchor, str): + raise TypeError("anchor must be a string") + anchor = anchor.lower() + if anchor == "auto": + anchor = "geometry_box" if cross_section.para_dim == 1 else "parametric" + if anchor not in {"parametric", "control_box", "geometry_box"}: + raise ValueError( + "anchor must be one of 'auto', 'parametric', " + "'control_box', or 'geometry_box'" + ) + + ### STARTING CALCULATIONS ### + + # make copies so we can work on it inplace + trajectory = trajectory.create.embedded(3) + cross_section = cross_section.create.embedded(3) + + # initialize parameter values + par_value = trajectory.greville_abscissae() + par_value = par_value.reshape(-1, 1) + + ### TRANSFORMATION MATRICES ### + + # tangent vector 'e1' of trajectory at parameter value 0 + e1 = trajectory.derivative([par_value[0]], [1]) + e1_norm = _np.linalg.norm(e1) + if e1_norm < _settings.TOLERANCE: + raise ValueError( + "Cannot determine initial sweep direction from trajectory " + "because the first tangent is too small." + ) + e1 = (e1 / e1_norm).ravel() + + # evaluating a vector normal to e1 + vec = [-e1[1], e1[0], -e1[2]] + B = [] + # avoid dividing by zero + temp_cross = _np.cross(e1, vec) + if _np.linalg.norm(temp_cross) > _settings.TOLERANCE: + B.append(temp_cross / _np.linalg.norm(temp_cross)) + else: + vec = [e1[2], -e1[1], e1[0]] + temp_cross = _np.cross(e1, vec) + B.append(temp_cross / _np.linalg.norm(temp_cross)) + # add debug message + _log.debug( + "The initial trajectory direction led to an ambiguous sweep " + "orientation. Using an alternative internal reference direction." + ) + + # initialize transformation matrices and tangent-vector-collection + T = [] + A = [] + tang_collection = [e1] + + # evaluating transformation matrices for each trajectory point + for i in range(len(par_value)): + # calculation according to NURBS Book, eq. 10.27 + # tangent vector e1 on trajectory at parameter value i + if i > 0: + e1 = trajectory.derivative([par_value[i]], [1]) + e1_norm = _np.linalg.norm(e1) + if e1_norm < _settings.TOLERANCE: + e1 = tang_collection[-1] + e1_norm = _np.linalg.norm(e1) + # add debug message + _log.debug( + "The trajectory tangent is too small at parametric value " + f"{par_value[i]}. Reusing the previous tangent direction " + "to continue the sweep frame construction." + ) + e1 = (e1 / e1_norm).ravel() + # collecting tangent vectors for later use + tang_collection.append(e1) + + # projecting B_(i) onto the plane normal to e1 + B.append(B[i] - _np.dot(B[i], e1) * e1) + if _np.linalg.norm(B[i + 1]) < _settings.TOLERANCE: + _log.warning( + "The automatically constructed sweep orientation became " + f"degenerate at parametric value {par_value[i]}. Applying a " + "small numerical adjustment to continue." + ) + B[i + 1] += _settings.TOLERANCE + B[i + 1] /= _np.linalg.norm(B[i + 1]) + + # defining e2 and e3 vectors + e3 = B[i + 1] + e2 = _np.cross(e3, e1) + + # array of transformation matrices from global to local coordinates + T.append(_np.vstack((e1, e2, e3))) + + # array of transformation matrices from local to global coordinates + A.append(T[i].T) + + # separate procedure, if trajectory is closed and B[0] != B[-1] + # recalculate B-vector and middle the values between B and B_rec + # according to NURBS Book, Piegl & Tiller, 2nd edition, p. 483 + is_trajectory_closed = _np.allclose( + trajectory.evaluate([[0]]), + trajectory.evaluate([par_value[-1]]), + atol=_settings.TOLERANCE, + rtol=1e-8, + ) + is_B_start_equal_B_end = _np.allclose( + B[0], B[-1], atol=_settings.TOLERANCE, rtol=1e-8 + ) + + if is_trajectory_closed and not is_B_start_equal_B_end: + # reset transformation matrices + T = [] + A = [] + # preallocate B_rec + B_rec = [None] * len(B) + # make sure start of B_rec is equal to the end of B + B_rec[0] = B[-1] + # redo the calculation of B using tang_collection from before + # in order to avoid recalculating the tangent vectors; + # calculation according to NURBS Book, eq. 10.27 + for i in range(len(par_value)): + B_rec[i + 1] = ( + B_rec[i] - _np.dot(B_rec[i], tang_collection[i]) * tang_collection[i] + ) + if _np.linalg.norm(B_rec[i + 1]) < _settings.TOLERANCE: + _log.warning( + "The corrected sweep orientation for the closed " + f"trajectory became degenerate at parametric value " + f"{par_value[i]}. Applying a small numerical adjustment " + "to continue." + ) + B_rec[i + 1] += _settings.TOLERANCE + B_rec[i + 1] /= _np.linalg.norm(B_rec[i + 1]) + # middle point between B and B_rec + B_rec[i + 1] = (B[i + 1] + B_rec[i + 1]) * 0.5 + # normalizing B_rec + B_rec[i + 1] /= _np.linalg.norm(B_rec[i + 1]) + # defining e2 and e3 axis-vectors + e3 = B_rec[i + 1] + e2 = _np.cross(e3, tang_collection[i]) + + # array of transformation matrices from global to local coordinates + T.append(_np.vstack((tang_collection[i], e2, e3))) + + # array of transformation matrices from local to global coordinates + A.append(T[i].T) + + # check if the beginning and the end of the B-vector are the same + if not _np.allclose(B_rec[0], B_rec[-1], rtol=1e-3): + _log.warning( + "The sweep orientation could only be matched approximately " + "because the closed trajectory has non-matching tangent " + "directions at its start and end." + ) + + ### ROTATION MATRIX ### + # rotates cross-section normal vector into global e1 direction + + # skip calculation if cross-section normal vector is default + if _np.array_equal(cross_section_normal, _np.array([0, 0, 1])): + R = _np.array([[0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [-1.0, 0.0, 0.0]]) + # else, calculate rotation matrix for given cross-section normal vector + else: + # calculate angle of cross-section normal vector around e2-axis + angle_of_cs_normal_1 = _np.arctan2( + cross_section_normal[2], cross_section_normal[0] + ) + + # calculate angle of cross-section normal vector around e3-axis + angle_of_cs_normal_2 = _np.arctan2( + cross_section_normal[1], cross_section_normal[2] + ) + + # calculate rotation matrix for cross-section normal vector + R1 = _arr.rotation_matrix_around_axis( + axis=[0, 1, 0], rotation=angle_of_cs_normal_1, degree=False + ) + R2 = _arr.rotation_matrix_around_axis( + axis=[0, 0, 1], rotation=angle_of_cs_normal_2, degree=False + ) + R = _np.matmul(R2, R1) + + # rotate cross-section around trajectory tangent vector (e1) if wanted + if rotation_adaption is not None: + R = _np.matmul( + _arr.rotation_matrix_around_axis( + axis=[1, 0, 0], rotation=rotation_adaption, degree=False + ), + R, + ) + + # remove numerical noise + R = _np.where(_np.abs(R) < _settings.TOLERANCE, 0, R) + + ### SWEEPING PROCESS ### + + # evaluate center of cross-section and translate to origin + if anchor == "parametric": + cross_para_center = _np.mean(cross_section.parametric_bounds, axis=0) + cs_center = cross_section.evaluate( + cross_para_center.reshape(-1, cross_section.para_dim) + ).ravel() + elif anchor == "control_box": + cs_min = cross_section.control_points.min(axis=0) + cs_max = cross_section.control_points.max(axis=0) + cs_center = 0.5 * (cs_min + cs_max) + else: # geometry_box + if cross_section.para_dim == 1: + sample_resolution = max(101, 4 * cross_section.control_points.shape[0]) + else: + sample_resolution = int(_np.ceil(625 ** (1 / cross_section.para_dim))) + sample_resolution = max(5, min(25, sample_resolution)) + + sample_axes = [ + _np.linspace( + cross_section.parametric_bounds[0, i], + cross_section.parametric_bounds[1, i], + sample_resolution, + ) + for i in range(cross_section.para_dim) + ] + sample_queries = _np.stack( + _np.meshgrid(*sample_axes, indexing="ij"), + axis=-1, + ).reshape(-1, cross_section.para_dim) + sampled_points = cross_section.evaluate(sample_queries) + cs_min = sampled_points.min(axis=0) + cs_max = sampled_points.max(axis=0) + cs_center = 0.5 * (cs_min + cs_max) + + centered_cross_section_cps = cross_section.control_points - cs_center + + # set cross-section control points along trajectory + swept_spline_cps = [] + rotated_cross_section_cps = centered_cross_section_cps @ R.T + for i, par_val in enumerate(par_value): + # evaluate trajectory if user wants to set cross-section on trajectory. + if set_on_trajectory: + section_origin = trajectory.evaluate([par_val]).ravel() + else: + section_origin = trajectory.control_points[i] + + swept_spline_cps.append(rotated_cross_section_cps @ A[i].T + section_origin) + + # create spline dictionary + dict_swept_spline = { + "degrees": [*cross_section.degrees, *trajectory.degrees], + "knot_vectors": [ + *cross_section.knot_vectors, + *trajectory.knot_vectors, + ], + "control_points": _np.asarray(swept_spline_cps).reshape(-1, cross_section.dim), + } + + # add weights properly if spline is rational + if cross_section.is_rational or trajectory.is_rational: + + def weights(spline): + if spline.is_rational: + return spline.weights + return _np.ones(spline.control_points.shape[0]) + + trajectory_weights = weights(trajectory) + cross_section_weights = weights(cross_section) + dict_swept_spline["weights"] = _np.outer( + trajectory_weights, cross_section_weights + ).reshape(-1, 1) + spline_type = _NURBS + else: + spline_type = _BSpline + + # create swept spline + swept_spline = spline_type(**dict_swept_spline) + + return swept_spline + + def from_bounds(parametric_bounds, physical_bounds): """Creates a minimal spline with given parametric bounds, physical bounds. Physical bounds can have less or equal number of @@ -406,6 +784,7 @@ def determinant_spline(spline): spline.unique_knots, spline.knot_multiplicities, multiplicity_increase, + strict=True, ): # increase knot multiplicities: # @ each inner knot -> mult_inc + 1 @@ -420,7 +799,9 @@ def determinant_spline(spline): [ len(kvs_ds) - d_ds - 1 for kvs_ds, d_ds in zip( - knot_vectors_determinant_spline, degrees_determinant_spline + knot_vectors_determinant_spline, + degrees_determinant_spline, + strict=True, ) ] ) @@ -478,9 +859,7 @@ def parametric_view(spline, axes=True, conform=False): para_spline: BSpline """ p_bounds = spline.parametric_bounds - para_spline = from_bounds( - parametric_bounds=p_bounds, physical_bounds=p_bounds - ) + para_spline = from_bounds(parametric_bounds=p_bounds, physical_bounds=p_bounds) # process to create conforming para_view splines if conform: @@ -506,7 +885,7 @@ def parametric_view(spline, axes=True, conform=False): # process knots to insert if spline.has_knot_vectors: for i, (kv, d) in enumerate( - zip(spline.knot_vectors, spline.degrees) + zip(spline.knot_vectors, spline.degrees, strict=True) ): n_repeating = int(d + 1) query = kv[n_repeating:-n_repeating] diff --git a/splinepy/helpme/integrate.py b/splinepy/helpme/integrate.py index 4fdec2440..c2c3fd3af 100644 --- a/splinepy/helpme/integrate.py +++ b/splinepy/helpme/integrate.py @@ -3,6 +3,11 @@ import numpy as _np from splinepy.utils.data import cartesian_product as _cartesian_product +from splinepy.utils.data import has_scipy as _has_scipy + +if _has_scipy: + from scipy.sparse import dok_matrix as _dok_matrix + from scipy.sparse.linalg import spsolve as _spsolve def _get_integral_measure(spline): @@ -15,7 +20,7 @@ def _get_integral_measure(spline): .. math:: \\mathcal{J}_S = det(\\mathbf(J)) - If the physical dimension is bigger then the paramtric dimension it will + If the physical dimension is bigger than the parametric dimension it will return .. math:: @@ -24,12 +29,13 @@ def _get_integral_measure(spline): Parameters ---------- spline : Spline / Multipatch - For parametric and physical dimension + The spline object for which the measure of one of its patches is determined Returns ------- measure : Callable - single patch only + A function which computes the jacobian's determinant for a single patch. + It takes a patch and query positions in the parametric domain as input """ # Check dimensionality if spline.dim == spline.para_dim: @@ -53,11 +59,25 @@ def measure(spline_patch, positions): raise ValueError("`Volume` not supported if para_dim > dim") +def _default_quadrature_orders(spline): + # Expected degree for quadrature based on spline's degrees and parametric dimension + expected_degree = spline.degrees * spline.para_dim + 1 + if spline.is_rational: + expected_degree += 2 + spline._logd("Integration on rational spline is only approximation") + + # Gauss-Legendre is exact for polynomials 2*n-1 + return _np.ceil((expected_degree + 1) * 0.5).astype("int") + + def _get_quadrature_information(spline, orders=None): """ - Select appropriate integration order (gauss-legendre) + Select appropriate integration order (gauss-legendre). - Determinante of a polynomial spline with para_dim==dim has degree + Determines the integration points and weights for numerical integration + based on the given spline and the optionally given quadrature orders. + + Determinant of a polynomial spline with para_dim==dim has degree .. math:: p_i^{det} = n_{dim} \\cdot p_i - 1 @@ -73,16 +93,17 @@ def _get_quadrature_information(spline, orders=None): Parameters ---------- spline : Spline - Spline for integration + The spline object for which the quadrature information is determined. orders : array-like (optional) - Orders along every parametric dimension + Orders along every parametric dimension. If not provided, default quadrature + orders will be used. Returns ------- positions : np.ndarray - quadrature position in unit-square + Quadrature points in unit-square weights : np.ndarray - quadrature weights + Quadrature weights """ # Determine integration points @@ -91,15 +112,8 @@ def _get_quadrature_information(spline, orders=None): # Determine integration orders if orders is None: - expected_degree = spline.degrees * spline.para_dim + 1 - if spline.is_rational: - expected_degree += 2 - spline._logd( - "Integration on rational spline is only approximation" - ) + quad_orders = _default_quadrature_orders(spline) - # Gauss-Legendre is exact for polynomials 2*n-1 - quad_orders = _np.ceil((expected_degree + 1) * 0.5).astype("int") else: quad_orders = _np.ascontiguousarray(orders, dtype=int).flatten() if quad_orders.size != spline.para_dim: @@ -124,22 +138,24 @@ def _get_quadrature_information(spline, orders=None): def volume(spline, orders=None): r"""Compute volume of a given spline + Uses quadrature to compute volume. Can handle patches with multiple elements. + Parameters ---------- spline : Spline (self if called via integrator) - splinepy - spline type + The spline object for which the volume is computed. orders : array-like (optional) order for gauss quadrature Returns ------- volume : float - Integral of dim-dimensional object + The computed volume of the spline """ from splinepy.spline import Spline as _Spline - # Check i_nput type + # Check input type if not isinstance(spline, _Spline): raise NotImplementedError("integration only works for splines") @@ -160,24 +176,22 @@ def volume(spline, orders=None): return volume -def _user_function( +def parametric_function( spline, function, orders=None, - physical=False, ): - """Integrate a function defined within the selected domain + """Integrate a function defined within the parametric domain Parameters ---------- spline : Spline - (self if called via integrator) + The geometry over which the function is integrated function : Callable + The user-defined function to integrate. It takes points in the parametric + dimension as input and outputs a scalar or an array of scalars. orders : optional - physical : bool - If True, the function is defined in the physical domain. If False, - the function is defined in the parametric domain. - Default is False. + Quadrature orders for numerical integration Returns ------- @@ -193,13 +207,6 @@ def _user_function( meas = _get_integral_measure(spline) positions, weights = _get_quadrature_information(spline, orders) - # define function to integrate - def _function(position): - if physical: - return function(spline.evaluate(position)) - else: - return function(position) - # Calculate Volume if spline.has_knot_vectors: # positions must be mapped into each knot-element @@ -209,73 +216,90 @@ def _function(position): initial = function([positions[0]]) result = _np.zeros(initial.shape[1]) for bezier_element in para_view.extract.beziers(): - # Get the bezier element scaling factor to get the correct - # element size from original knot element - knot_element_scaling_factor = _np.prod( - _np.diff(bezier_element.control_point_bounds, axis=0) - ) quad_positions = bezier_element.evaluate(positions) result += _np.einsum( "i...,i,i->...", - _function(quad_positions), - meas(spline, quad_positions) * knot_element_scaling_factor, + function(quad_positions), + meas(spline, quad_positions), weights, optimize=True, ) + else: result = _np.einsum( "i...,i,i->...", - _function(positions), + function(positions), meas(spline, positions), weights, - optimize=True, ) return result -def parametric_function( +def physical_function( spline, function, orders=None, ): - """Integrate a function defined within the parametric domain + """Integrate a function defined within the selected domain Parameters ---------- spline : Spline - (self if called via integrator) + The geometry over which the function is integrated function : Callable + The user-defined function to integrate. orders : optional + Quadrature order in parametric domain for numerical integration Returns ------- integral : np.ndarray """ - return _user_function(spline, function, orders=orders, physical=False) + from splinepy.spline import Spline as _Spline + # Check input type + if not isinstance(spline, _Spline): + raise NotImplementedError("integration only works for splines") -def physical_function( - spline, - function, - orders=None, -): - """Integrate a function defined within the physical domain + # Retrieve aux info + meas = _get_integral_measure(spline) + positions, weights = _get_quadrature_information(spline, orders) - Parameters - ---------- - spline : Spline - The geometry over which the function is integrated - function : Callable - The user-defined function to integrate. Can also be vector-valued - orders : optional - Quadrature order in parametric domain for numerical integration + # define function to integrate + def _function(position): + return function(spline.evaluate(position)) - Returns - ------- - integral : np.ndarray - The computed integral. It is vector-valued if function is vector-valued - """ - return _user_function(spline, function, orders=orders, physical=True) + # Calculate Volume + if spline.has_knot_vectors: + # positions must be mapped into each knot-element + para_view = spline.create.parametric_view(axes=False) + + # get initial shape + initial = function([positions[0]]) + result = _np.zeros(initial.shape[1]) + for bezier_element in para_view.extract.beziers(): + # Get the bezier element scaling factor to get the correct + # element size from original knot element + knot_element_scaling_factor = _np.prod( + _np.diff(bezier_element.control_point_bounds, axis=0) + ) + quad_positions = bezier_element.evaluate(positions) + result += _np.einsum( + "i...,i,i->...", + _function(quad_positions), + meas(spline, quad_positions) * knot_element_scaling_factor, + weights, + optimize=True, + ) + else: + result = _np.einsum( + "i...,i,i->...", + _function(positions), + meas(spline, positions), + weights, + optimize=True, + ) + return result class Integrator: @@ -312,3 +336,1015 @@ def parametric_function(self, *args, **kwargs): @_wraps(physical_function) def physical_function(self, *args, **kwargs): return physical_function(self._helpee, *args, **kwargs) + + +class Transformation: + """Helper class to be used for the numerical integration of field variables on a + single-patch geometry. + + It computes the elements within the patch and for each one of them the quadrature + points and the Jacobian at those points. + + Parameters + ---------- + spline: spline + The geometry + solution_field: None or spline + Solution field as spline function. If not given, supports and quadrature + will be calculated for geometry + orders: None or list + Quadrature orders. If not given, default quadrature orders will be used + """ + + __slots__ = ( + "_spline", + "_solution_field", + "_mapper", + "_para_dim", + "_ukv", + "_n_elems", + "_quad_positions", + "_quad_weights", + "_grid_ids", + "_all_supports", + "_all_element_quad_points", + "_all_jacobians", + "_all_jacobian_inverses", + "_all_jacobian_determinants", + "_all_element_measures", + ) + + def __init__(self, spline, solution_field=None, orders=None): + self._spline = spline + self._solution_field = solution_field + if solution_field is not None: + self._mapper = self._solution_field.mapper(reference=self._spline) + + self._para_dim = spline.para_dim + if self._para_dim == 3: + raise NotImplementedError("Not yet tested for 3D") + + if solution_field is None: + self._ukv = spline.unique_knots + else: + self._ukv = self._solution_field.unique_knots + n_elems_per_dim = [len(kv) - 1 for kv in self._ukv] + self._n_elems = _np.prod(n_elems_per_dim) + + # Gauss-Legendre quadrature points and weights + spline_for_quad = spline if solution_field is None else solution_field + + if orders is None: + quad_positions = [] + quad_weights = [] + for dim_quadrature_order in _default_quadrature_orders( + spline_for_quad + ): + quad_position, quad_weight = _np.polynomial.legendre.leggauss( + deg=dim_quadrature_order + ) + # Make quadrature points go from [0,1] instead of [-1,1] + quad_positions.append((quad_position + 1) / 2) + # Adjust weights accordingly + quad_weights.append(quad_weight / 2) + + self._quad_positions = _cartesian_product(quad_positions) + self._quad_weights = _np.prod( + _cartesian_product(quad_weights), axis=1 + ) + else: + self._quad_positions, self._quad_weights = ( + _get_quadrature_information(spline_for_quad, orders) + ) + + # Precompute grid IDs + self._grid_ids = _cartesian_product( + [_np.arange(n_elems) for n_elems in n_elems_per_dim], + reverse=True, + ) + + self._all_supports = None + self._all_element_quad_points = None + self._all_jacobians = None + self._all_jacobian_inverses = None + self._all_jacobian_determinants = None + self._all_element_measures = None + + def check_element_id_validity(self, element_id): + """Check if given element ID is valid + + Parameters + ----------- + element_id: int + ID of element in spline's element. ID-array is 1D + """ + assert element_id >= 0 + assert element_id < self._n_elems + + @property + def all_supports(self): + """Supports of all quadrature points. + List of entries of support""" + return self._all_supports + + @property + def all_quad_points(self): + """Quadrature points of all elements. + Dimensions [, , 2]""" + return self._all_element_quad_points + + @property + def all_jacobians(self): + """Jacobians of all elements. + Dimensions [ , , ]""" + return self._all_jacobians + + @property + def all_jacobian_inverses(self): + """Inverses of Jacobians of all elements. + Dimensions [ , , ]""" + return self._all_jacobian_inverses + + @property + def all_jacobian_determinants(self): + """Determinants of Jacobians of all elements. + Dimensions [ ]""" + return self._all_jacobian_determinants + + @property + def quadrature_weights(self): + return self._quad_weights + + def get_element_grid_id(self, element_id): + """Compute element ID in grid. + + The grid follows splinepy's ordering: first it goes into the x-direction, + after that into the y-direction. + + Parameters + ---------- + element_id: int + ID of spline's element in element grid + + Returns + --------- + element_grid_id: list + The grid ID of the element. + """ + if self._para_dim == 3: + raise NotImplementedError( + "Element grid ID not yet implemented for 3D" + ) + + return self._grid_ids[element_id, :] + + def get_element_quad_points(self, element_id): + """Compute the quadrature points for a given element. + + Parameters + ----------- + element_id: int + ID of spline's element in element grid + + Returns + ----------- + element_quad_points: np.ndarray + Quadrature points for element + """ + self.check_element_id_validity(element_id) + + if self._all_element_quad_points is not None: + return self._all_element_quad_points[element_id] + + element_grid_id = self.get_element_grid_id(element_id) + + element_corner_points = _np.vstack( + [ + ukv_dim[e_dim_id : (e_dim_id + 2)] + for ukv_dim, e_dim_id in zip( + self._ukv, element_grid_id, strict=True + ) + ] + ) + element_lengths = _np.diff(element_corner_points, axis=1).ravel() + element_midpoints = _np.mean(element_corner_points, axis=1) + + # Bring center to origin and scale + element_quad_points = (self._quad_positions - 0.5) * element_lengths + # Apply offset + element_quad_points += element_midpoints + + return element_quad_points + + def get_element_support(self, element_id): + """Get support for quadrature points in element + + Parameters + ------------ + element_id: int + ID of spline's element in element grid + + Returns + --------- + support: np.ndarray + Support for element. All quadrature points have same support + """ + element_quad_points = self.get_element_quad_points(element_id) + + # All quad points in element have same support, therefore take arbitrary + # one + relevant_quad_point = element_quad_points[0, :] + + if self._solution_field is None: + return self._spline.support(relevant_quad_point) + else: + return self._solution_field.support(relevant_quad_point) + + def jacobian(self, element_id): + """Return Jacobian of single element at quadrature points + + Parameters + ------------ + element_id: list + ID of spline's element in element grid + + Returns + --------- + element_jacobian: np.ndarray + Jacobian of element evaluated at quadrature points + """ + if self._all_jacobians is not None: + return self._all_jacobians[element_id] + + element_quad_points = self.get_element_quad_points(element_id) + + return self._spline.jacobian(element_quad_points) + + def jacobian_inverse(self, element_id): + """Return inverse of Jacobian of single element, evaluated at quadrature points + + Parameters + ------------ + element_id: list + ID of spline's element in element grid + + Returns + --------- + element_inverse_jacobian: np.ndarray + Inverse of Jacobian of element evaluated at quadrature points + """ + if self._all_jacobian_inverses is not None: + return self._all_jacobian_inverses[element_id] + + element_jacobians = self.jacobian(element_id) + element_jacobian_inverse = _np.stack( + [ + _np.linalg.inv(element_jacobian) + for element_jacobian in element_jacobians + ] + ) + + return element_jacobian_inverse + + def jacobian_determinant(self, element_id): + """Return determinant of Jacobian of single element, evaluated at + quadrature points + + Parameters + ------------ + element_id: list + ID of element in grid + + Returns + --------- + element_jacobian_determinant: np.ndarray + Determinant of Jacobian of element evaluated at quadrature points + """ + if self._all_jacobian_determinants is not None: + return self._all_jacobian_determinants[element_id] + + element_jacobians = self.jacobian(element_id) + return _np.array( + [ + _np.linalg.det(element_jacobian) + for element_jacobian in element_jacobians + ] + ) + + def compute_all_element_quad_points(self, recompute=False): + """Compute the quadrature points of all elements + + Parameters + ---------- + recompute: bool (optional) + If True, recompute the qudrature points. Default is no recomputation + """ + if self._all_element_quad_points is not None and not recompute: + return + + # compute element lengths and center points + element_lengths = _cartesian_product( + [_np.diff(dim_ukv) for dim_ukv in self._ukv] + ) + element_midpoints = ( + _cartesian_product([dim_ukv[:-1] for dim_ukv in self._ukv]) + + element_lengths / 2 + ) + # Scale quad points for each element + quad_points_centered = _np.einsum( + "ij,hj->hij", self._quad_positions, element_lengths + ) + # apply offset to quad points + n_elements, n_quad_points, _ = quad_points_centered.shape + offsets = element_midpoints - element_lengths / 2 + self._all_element_quad_points = quad_points_centered + _np.repeat( + (offsets).reshape(n_elements, 1, -1), n_quad_points, 1 + ) + + def compute_all_supports(self, recompute=False): + """Compute the support for all quadrature points + + Parameters + -------------- + recompute: bool (optional) + If True, recomputes the supports. The default is no recomputation + """ + if self._all_supports is not None and not recompute: + return + + self.compute_all_element_quad_points(recompute=recompute) + relevant_spline = ( + self._spline + if self._solution_field is None + else self._solution_field + ) + self._all_supports = [ + relevant_spline.support(quad_points[:1, :]).ravel() + for quad_points in self._all_element_quad_points + ] + + def compute_all_element_jacobians(self, recompute=False): + """Compute Jacobians of each element at each quadrature point + + Parameters + ---------- + recompute: bool (optional) + If True, recomputes the Jacobians. Default option is no recomputation + """ + if self._all_jacobians is not None and not recompute: + return + + self.compute_all_element_quad_points(recompute=recompute) + self._all_jacobians = _np.stack( + [ + self._spline.jacobian(quad_points) + for quad_points in self._all_element_quad_points + ] + ) + + def compute_all_element_jacobian_inverses(self, recompute=False): + """Compute Jacobians' inverses of each element at each quadrature point + + Parameters + ---------- + recompute: bool (optional) + If True, recompute Jacobians' inverses. Default is no recomputation + """ + if self._all_jacobian_inverses is not None and not recompute: + return + + self.compute_all_element_jacobians(recompute=recompute) + + self._all_jacobian_inverses = _np.stack( + [ + _np.stack( + [ + _np.linalg.inv(element_jacobian) + for element_jacobian in element_jacobians + ] + ) + for element_jacobians in self._all_jacobians + ] + ) + + def compute_all_element_jacobian_determinants(self, recompute=False): + """Compute Jacobians' determinants of each element at each quadrature point + + Parameters + ---------- + recompute: bool + Recompute Jacobians' determinants + """ + if self._all_jacobian_determinants is not None and not recompute: + return + + self.compute_all_element_jacobians(recompute=recompute) + + self._all_jacobian_determinants = _np.stack( + [ + _np.stack( + [ + _np.linalg.det(element_jacobian) + for element_jacobian in element_jacobians + ] + ) + for element_jacobians in self._all_jacobians + ] + ) + + def compute_all_element_measures(self, recompute=False): + """Computes the measures of all elements in parametric space. Assumes + tensor-product-like structure. + """ + if self._all_element_measures is not None and not recompute: + return + + element_lengths = [_np.diff(ukv) for ukv in self._ukv] + self._all_element_measures = _np.prod( + _cartesian_product(element_lengths), axis=1 + ) + + +class FieldIntegrator: + """ + Class for the numerical evaluation of a PDE on a single-patch geometry. + + On initialization it sets up solution field, its mapper, precomputes the + transformation and calculate the number of DoFs. + + + + Parameters + ---------- + geometry: spline + The geometry + solution_field: None or spline + Solution field. If not given, quadrature and supports will be calculated + using the geometry + orders: None or list + Quadrature order in each dimension. If not given, default quadrature + will be used + """ + + __slots__ = ( + "_helpee", + "_solution_field", + "_mapper", + "_global_rhs", + "_global_system_matrix", + "_trafo", + "_supports", + "_ndofs", + ) + + def __init__(self, geometry, solution_field=None, orders=None): + self._helpee = geometry + if solution_field is None: + self._solution_field = geometry.copy() + self._solution_field.control_points = _np.zeros( + (geometry.cps.shape[0], 1) + ) + else: + self._solution_field = solution_field + self._ndofs = int( + _np.prod( + [ + len(kv) - 1 - deg + for deg, kv in zip( + self._solution_field.degrees, + self._solution_field.knot_vectors, + strict=True, + ) + ] + ) + ) + self._mapper = self._solution_field.mapper(reference=self._helpee) + + self.reset(orders) + + def reset(self, orders=None): + """Sets up the transformation and resets the lhs and rhs. + + Parameters + ------------ + orders: None or list + If given, these orders will be used for quadrature. Otherwise, default + quadrature orders will be used. + """ + self._trafo = Transformation( + self._helpee, self._solution_field, orders + ) + self.precompute_transformation() + + self._supports = None + self._global_rhs = None + self._global_system_matrix = None + + def precompute_transformation(self): + """Computes the quadrature points, jacobians and their determinants + of all elements in spline""" + self._trafo.compute_all_supports() + self._trafo.compute_all_element_jacobian_determinants() + + @property + def supports(self): + """ + Get the quadrature points' supports. + + Returns + ------- + supports: np.ndarray + The supports + """ + if self._supports is None: + self._supports = self._helpee.supports(self._trafo.all_quad_points) + + return self._supports + + def assemble_matrix(self, function, matrixout=None): + """Assemble the system matrix for a given function. If system matrix is + already assembled, it will add values on top of existing matrix. + + Parameters + ------------ + function: callable + Function which defines how to assemble an element matrix + matrixout: np.ndarray / scipy.sparse matrix + Assembled matrix will be stored there. Default is global system matrix + """ + # Initialize system matrix if not already + if self._global_system_matrix is None and matrixout is None: + global_size = (self._ndofs, self._ndofs) + if _has_scipy: + self._global_system_matrix = _dok_matrix(global_size) + else: + self._global_system_matrix = _np.zeros(global_size) + + # If other matrix is used, check if it compatible + if matrixout is not None: + if _has_scipy: + assert isinstance( + matrixout, _dok_matrix + ), "Matrixout must be scipy sparse dok matrix" + else: + assert isinstance(matrixout, _np.ndarray) + assert matrixout.shape == (self._ndofs, self._ndofs) + + system_matrix = ( + self._global_system_matrix if matrixout is None else matrixout + ) + + quad_weights = self._trafo.quadrature_weights + + # Element loop + for element_jacobian_det, element_support, element_quad_points in zip( + self._trafo.all_jacobian_determinants, + self._trafo.all_supports, + self._trafo.all_quad_points, + strict=True, + ): + element_matrix = function( + mapper=self._mapper, + quad_points=element_quad_points, + quad_weights=quad_weights, + jacobian_det=element_jacobian_det, + ) + matrix_element_support = _cartesian_product( + [element_support, element_support] + ) + system_matrix[ + matrix_element_support[:, 0], matrix_element_support[:, 1] + ] += element_matrix + + def assemble_vector(self, function, current_sol=None, vectorout=None): + """Assemble the rhs for a given function. If rhs is already assembled, + it will add values on top of existing rhs. + + Parameters + ------------ + function: callable + Function which defines how to assemble an element vector + current_sol: np.ndarray + Current solution vector. Needed for nonlinear forms + vectorout: np.ndarray + Assembled rhs vector will be stored there. Default is global rhs + """ + # Initialize rhs vector + if self._global_rhs is None: + self._global_rhs = _np.zeros(self._ndofs) + + # Ensure that current solution has right dimensions + if current_sol is not None: + assert len(current_sol) == self._ndofs + + if vectorout is not None: + assert isinstance(vectorout, _np.ndarray) + assert len(vectorout) == self._ndofs + + rhs_vector = self._global_rhs if vectorout is None else vectorout + + # Prepare function arguments, which stay the same for all elements + function_args = { + "mapper": self._mapper, + "quad_weights": self._trafo.quadrature_weights, + } + + # Element loop + for element_jacobian_det, element_support, element_quad_points in zip( + self._trafo.all_jacobian_determinants, + self._trafo.all_supports, + self._trafo.all_quad_points, + strict=True, + ): + # Assemble element vector + function_args["quad_points"] = element_quad_points + function_args["jacobian_det"] = element_jacobian_det + if current_sol is not None: + function_args["current_sol"] = current_sol[element_support] + element_vector = function(**function_args) + + # Add element vector to global rhs vector + rhs_vector[element_support] += element_vector + + def assemble_matrix_and_vector( + self, function, current_sol=None, matrixout=None, vectorout=None + ): + """Assemble the system matrix and rhs vector for a given function. If system + matrix is already assembled, it will add values on top of existing matrix. + The same goes for the rhs vector + + Parameters + ------------ + function: callable + Function which defines how to assemble an element matrix and vector + current_sol: np.ndarray + Current solution vector. Needed for nonlinear forms + matrixout: np.ndarray / scipy.sparse matrix + Assembled matrix will be stored there. Default is global system matrix + vectorout: np.ndarray + Assembled rhs vector will be stored there. Default is global rhs + """ + # Initialize system matrix if not already + if self._global_system_matrix is None and matrixout is None: + global_size = (self._ndofs, self._ndofs) + if _has_scipy: + self._global_system_matrix = _dok_matrix(global_size) + else: + self._global_system_matrix = _np.zeros(global_size) + + # If other matrix is used, check if it compatible + if matrixout is not None: + if _has_scipy: + assert isinstance( + matrixout, _dok_matrix + ), "Matrixout must be scipy sparse dok matrix" + else: + assert isinstance(matrixout, _np.ndarray) + assert matrixout.shape == (self._ndofs, self._ndofs) + + # Set matrix accordingly + system_matrix = ( + self._global_system_matrix if matrixout is None else matrixout + ) + + # Initialize rhs vector + if self._global_rhs is None: + self._global_rhs = _np.zeros(self._ndofs) + + # Ensure that current solution has right dimensions + if current_sol is not None: + assert len(current_sol) == self._ndofs + + if vectorout is not None: + assert isinstance(vectorout, _np.ndarray) + assert len(vectorout) == self._ndofs + + # Set rhs vector accordingly + rhs_vector = self._global_rhs if vectorout is None else vectorout + + # Prepare function arguments, which stay the same for all elements + function_args = { + "mapper": self._mapper, + "quad_weights": self._trafo.quadrature_weights, + } + + # Element loop + for element_jacobian_det, element_support, element_quad_points in zip( + self._trafo.all_jacobian_determinants, + self._trafo.all_supports, + self._trafo.all_quad_points, + strict=True, + ): + # Assemble element matrix and vector + function_args["quad_points"] = element_quad_points + function_args["jacobian_det"] = element_jacobian_det + if current_sol is not None: + function_args["current_sol"] = current_sol[element_support] + element_matrix, element_vector = function(**function_args) + + # Add element vector to global system matrix + matrix_element_support = _cartesian_product( + [element_support, element_support] + ) + system_matrix[ + matrix_element_support[:, 0], matrix_element_support[:, 1] + ] += element_matrix + + # Add element vector to global rhs vector + rhs_vector[element_support] += element_vector + + def L2_projection(self, function): + """Perform an L2-projection of a function + + Parameters + ---------------- + function: callable + Function to L2-project + + Returns + ------------- + dof_values: np.ndarray + L2-projected values for Dofs + """ + + def dirichlet_lhs_and_rhs( + mapper, quad_points, quad_weights, jacobian_det + ): + # Assemble system matrix + bf_values = mapper._field_reference.basis(quad_points) + element_matrix = _np.einsum( + "qi,qj,q,q->ij", + bf_values, + bf_values, + quad_weights, + jacobian_det, + optimize=True, + ) + + # Assemble rhs + quad_points_forward = mapper._geometry_reference.evaluate( + quad_points + ) + function_values = function(quad_points_forward) + element_vector = _np.einsum( + "qj,q,q,q->j", + bf_values, + function_values, + quad_weights, + jacobian_det, + optimize=True, + ) + + return element_matrix.ravel(), element_vector + + # Initialiye mass matrix and rhs + global_size = (self._ndofs, self._ndofs) + mass_matrix = ( + _dok_matrix(global_size) if _has_scipy else _np.zeros(global_size) + ) + rhs_vector = _np.zeros(self._ndofs) + # Assemble lhs and rhs + self.assemble_matrix_and_vector( + dirichlet_lhs_and_rhs, matrixout=mass_matrix, vectorout=rhs_vector + ) + + # Solve system to get all dofs + dof_values = _np.empty(self._ndofs) + if _has_scipy: + dof_values = _spsolve(mass_matrix.tocsr(), rhs_vector) + else: + dof_values = _np.linalg.solve(mass_matrix, rhs_vector) + + return dof_values + + def check_if_assembled(self): + """ + Check if system matrix and rhs are already assembled + """ + if self._global_system_matrix is None or self._global_rhs is None: + raise ValueError("System is not yet fully assembled") + + def get_boundary_dofs( + self, + all_boundaries=True, + north=False, + east=False, + south=False, + west=False, + ): + """ + Get indices of boundary dofs. Assumes that control points are arranged + in the following way: first one is the southwest corner, then the first + dimension goes from west to east and second dimension goes from south to + north. + + Parameters + ------------------ + all_boundaries: bool + If True, get indices of all boundary dofs and ignores values for + north, south, east and west. On the other, if any boundary is selected, + this value will be ignored + north: bool + If True, return indices for north boundary of geometry + east: bool + south: bool + west: bool + + Returns + ------------- + indices: np.ndarray + Indices of relevant boundary dofs + """ + relevant_spline = ( + self._helpee + if self._solution_field is None + else self._solution_field + ) + + multi_index = relevant_spline.multi_index + + multi_indices = [ + multi_index[0, :], + multi_index[-1, :], + multi_index[:, 0], + multi_index[:, -1], + ] + + # If at least one boundary is True, set all_boundaries to False + if north or east or south or west: + all_boundaries = False + + boundaries = ( + [True] * 4 if all_boundaries else [west, east, south, north] + ) + + indices = _np.unique( + _np.hstack( + [ + index + for index, boundary in zip( + multi_indices, boundaries, strict=True + ) + if boundary + ] + ) + ) + + return indices + + def apply_homogeneous_dirichlet_boundary_conditions( + self, + all_boundaries=True, + north=False, + east=False, + south=False, + west=False, + ): + """ + Assembles homogeneous Dirichlet boundary conditions + + Parameters + ------------- + all_boundaries: bool + If True, get indices of all boundary dofs and ignores values for + north, south, east and west + north: bool + If True, sets homogeneous Dirichlet condition on north boundary + east: bool + south: bool + west: bool + """ + self.check_if_assembled() + + indices = self.get_boundary_dofs( + all_boundaries, north, east, south, west + ) + + self._global_system_matrix[indices, :] = 0 + self._global_system_matrix[indices, indices] = 1 + self._global_rhs[indices] = 0 + + def apply_dirichlet_boundary_conditions( + self, + function, + return_values=False, + all_boundaries=True, + north=False, + east=False, + south=False, + west=False, + ): + """ + Applies Dirichlet boundary conditions via L2-projection. + + Firstly, the function is L2-projected on the whole domain, but only the DoFs + corresponding to the boundaries are taken into account. + + Parameters + ------------- + function: callable + Function to apply. Input are points, output is scalar + return_values: bool + If True, computed values and the corresponding DoF indices will be returned + and not applied to global system matrix or the global rhs. If False, values + will be applied to system matrix and rhs and nothing will be returned + all_boundaries: bool + If True, get indices of all boundary dofs and ignores values for + north, south, east and west + north: bool + If True, sets homogeneous Dirichlet condition on north boundary + east: bool + south: bool + west: bool + """ + if not return_values: + self.check_if_assembled() + + dof_vector = self.L2_projection(function) + + # Get relevant dofs + indices = self.get_boundary_dofs( + all_boundaries, north, east, south, west + ) + + if return_values: + return dof_vector[indices], indices + else: + # Apply Dirichlet to relevant boundary dofs + self._global_system_matrix[indices, :] = 0 + self._global_system_matrix[indices, indices] = 1 + self._global_rhs[indices] = dof_vector[indices] + + def solve_linear_system(self): + """ + Solve linear system for system matrix and rhs + """ + self.check_if_assembled() + + if _has_scipy: + solution_vector = _spsolve( + self._global_system_matrix.tocsr(), self._global_rhs + ) + else: + solution_vector = _np.linalg.solve( + self._global_system_matrix, self._global_rhs + ) + self._solution_field.control_points = solution_vector.reshape(-1, 1) + + def compute_error(self, function, norm="l2"): + """ + Compute error to some given function(s) w.r.t a norm + + Parameters + ------------- + function: callable + Analytical function(s) to compare to + norm: str + Used norm for error calculation + + Returns + ------------------ + error_integration: np.ndarray + Value(s) of error in given norm + """ + self._trafo.compute_all_element_jacobian_determinants() + + # Evaluate function and solution values + all_quad_points = self._trafo._all_element_quad_points.reshape( + -1, self._helpee.para_dim + ) + all_physical_points = self._helpee.evaluate(all_quad_points) + all_function_values = function(all_physical_points) + solution_values = self._solution_field.evaluate(all_quad_points) + error_values = all_function_values - solution_values + # Take the norm into consideration + if norm == "l1": + error_norm_values = _np.abs(error_values) + elif norm == "l2": + error_norm_values = _np.power(error_values, 2) + elif norm == "linf": + return _np.max(_np.abs(error_values)) + else: + raise NotImplementedError(f"{norm}-norm not implemented") + + # Integrate error + quad_weights = self._trafo._quad_weights + n_weights = quad_weights.shape[0] + quad_weights = _np.tile( + quad_weights, len(solution_values) // len(quad_weights) + ) + self._trafo.compute_all_element_measures() + + error_integration = _np.einsum( + "i...,i,i->...", + error_norm_values, + self._trafo._all_jacobian_determinants.ravel() + * _np.repeat(self._trafo._all_element_measures, n_weights), + quad_weights, + optimize=True, + ) + + if norm == "l1": + return error_integration + elif norm == "l2": + return _np.sqrt(error_integration) diff --git a/splinepy/io/gismo.py b/splinepy/io/gismo.py index ec66f5662..9b1405540 100644 --- a/splinepy/io/gismo.py +++ b/splinepy/io/gismo.py @@ -270,7 +270,7 @@ def add_assembly_options( children_list = [] for label, description, value, number_type in zip( - labels, descriptions, values, number_types + labels, descriptions, values, number_types, strict=True ): children_list.append( { @@ -644,7 +644,9 @@ def export( boundary_data.text = "\n".join( [ str(patch_id + index_offset) + " " + str(local_face_id + 1) - for (patch_id, local_face_id) in zip(*face_id_list) + for (patch_id, local_face_id) in zip( + *face_id_list, strict=True + ) ] ) else: @@ -657,7 +659,9 @@ def export( boundary_data.text = "\n".join( [ str(sid + index_offset) + " " + str(bid + 1) - for (sid, bid) in zip(boundary_spline, boundary_face) + for (sid, bid) in zip( + boundary_spline, boundary_face, strict=True + ) ] ) ### @@ -685,7 +689,9 @@ def export( bc.text = "\n".join( [ str(sid) + " " + str(bid + 1) - for (sid, bid) in zip(bc_data_i[0], bc_data_i[1]) + for (sid, bid) in zip( + bc_data_i[0], bc_data_i[1], strict=True + ) ] ) @@ -704,9 +710,9 @@ def export( as_base64=as_base64, field_mask=field_mask, ) - field_xml.find("MultiPatch").find("patches").text = ( - f"{index_offset} " f"{n_patches - 1 + index_offset}" - ) + field_xml.find("MultiPatch").find( + "patches" + ).text = f"{index_offset} {n_patches - 1 + index_offset}" if int(_python_version.split(".")[1]) >= 9 and indent: _ET.indent(field_xml) file_content = _ET.tostring(field_xml) @@ -723,7 +729,7 @@ def export( if n_patches != len(multipatch.patches): raise RuntimeError("Help - some patches were not recognised") - patch_range.text = f"{index_offset} " f"{n_patches - 1 + index_offset}" + patch_range.text = f"{index_offset} {n_patches - 1 + index_offset}" # Add additional options to the xml file if additional_blocks is not None: @@ -976,12 +982,11 @@ def make_dictionary(ETelement): list_of_options.append(make_dictionary(child)) else: _debug( - f"Found unsupported keyword {child.tag}, which will be" - " ignored" + f"Found unsupported keyword {child.tag}, which will be ignored" ) continue - _debug(f"Found a total of {len(list_of_splines)} " f"BSplines and NURBS") + _debug(f"Found a total of {len(list_of_splines)} BSplines and NURBS") multipatch = _Multipatch(list_of_splines) if interface_array is not None: if invalid_integer in interface_array: diff --git a/splinepy/io/irit.py b/splinepy/io/irit.py index b00dca1d7..6fe88983e 100644 --- a/splinepy/io/irit.py +++ b/splinepy/io/irit.py @@ -62,7 +62,9 @@ def extract_relevant_text(lines): spline_list = [] # Loop over text file data - for type, data in zip(spline_strings[1::2], spline_strings[2::2]): + for type, data in zip( + spline_strings[1::2], spline_strings[2::2], strict=True + ): # Dictionary of current spline spline = {} diff --git a/splinepy/io/mfem.py b/splinepy/io/mfem.py index 750c9b951..f2d2cdec3 100644 --- a/splinepy/io/mfem.py +++ b/splinepy/io/mfem.py @@ -448,6 +448,7 @@ def _corner_vertex_ids(spline): for row, boundary_id in zip( boundaries.reshape(-1, n_vertex_per_boundary).tolist(), boundary_ids.tolist(), + strict=True, ) ) ) @@ -456,7 +457,7 @@ def _corner_vertex_ids(spline): f.write(f"\n\nedges\n{0}\n") # Write Number Of vertices - f.write(f"\nvertices\n{int(_np.max(vertex_ids)+1)}\n\n") + f.write(f"\nvertices\n{int(_np.max(vertex_ids) + 1)}\n\n") # Export Splines f.write("patches\n\n") @@ -497,6 +498,7 @@ def _corner_vertex_ids(spline): for (coords, weight) in zip( spline.control_points.tolist(), spline.weights.tolist(), + strict=True, ) ) + "\n\n" diff --git a/splinepy/io/svg.py b/splinepy/io/svg.py index b8fd6e40b..93d704f2a 100644 --- a/splinepy/io/svg.py +++ b/splinepy/io/svg.py @@ -271,7 +271,7 @@ def _export_gustaf_object( cmap_style = gus_object.show_options.get("cmap", "jet") colors = [ _rgb_2_hex(r, g, b) - for r, g, b, in _color_map( + for r, g, b in _color_map( values.ravel(), name=cmap_style, vmin=v_min, vmax=v_max ) ] @@ -284,7 +284,7 @@ def _export_gustaf_object( id="control_points", ) - for vertex, color in zip(gus_object.vertices, colors): + for vertex, color in zip(gus_object.vertices, colors, strict=True): _ET.SubElement( svg_control_points, "circle", @@ -320,7 +320,7 @@ def _export_gustaf_object( svg_labels.attrib["stroke"] = "none" dx = radius - for ctp, label in zip(gus_object.vertices, labels): + for ctp, label in zip(gus_object.vertices, labels, strict=True): text_element = _ET.SubElement( svg_labels, "text", @@ -395,7 +395,7 @@ def _export_control_mesh( "g", id="mesh", style=( - f"fill:none;stroke:{_rgb_2_hex(r,g,b)};stroke-opacity:{a};" + f"fill:none;stroke:{_rgb_2_hex(r, g, b)};stroke-opacity:{a};" f"stroke-width:{stroke_width};stroke-linecap:round" ), ) @@ -442,7 +442,6 @@ def _export_control_mesh( # Then control points if control_point_requested: - # Relevant options: # - control_point_ids # - control_point_alpha @@ -735,7 +734,7 @@ def _add_scalar_bar(svg_element, box_size, **kwargs): "g", id="tick_mark_lines", style=( - f"fill:none;stroke:{_rgb_2_hex(r,g,b)};stroke-opacity:{a};" + f"fill:none;stroke:{_rgb_2_hex(r, g, b)};stroke-opacity:{a};" f"stroke-width:{stroke_width};stroke-linecap:round" ), ) @@ -775,7 +774,7 @@ def _add_scalar_bar(svg_element, box_size, **kwargs): ), ) - for mark, position in zip(tick_marks, tick_mark_positions): + for mark, position in zip(tick_marks, tick_mark_positions, strict=True): # Draw a line _ET.SubElement( svg_tick_marks, @@ -987,7 +986,6 @@ def _approximate_curve(original_spline, tolerance): # Check if a field is to be plotted if spline.show_options.get("data", None) is not None: - # spline.show() _export_spline_field( spline, svg_spline, box_min_x, box_max_y, **kwargs @@ -1008,15 +1006,15 @@ def _approximate_curve(original_spline, tolerance): spline_copy = _approximate_curve(spline, tolerance) bezier_elements = spline_copy.extract.beziers() path_d = ( - f"M {bezier_elements[0].cps[0,0]-box_min_x}," - f"{box_max_y - bezier_elements[0].cps[0,1]}" + f"M {bezier_elements[0].cps[0, 0] - box_min_x}," + f"{box_max_y - bezier_elements[0].cps[0, 1]}" ) path_d += " ".join( [ ( - f" C {s.cps[1,0]-box_min_x},{box_max_y - s.cps[1,1]}" - f" {s.cps[2,0]-box_min_x},{box_max_y - s.cps[2,1]}" - f" {s.cps[3,0]-box_min_x},{box_max_y - s.cps[3,1]}" + f" C {s.cps[1, 0] - box_min_x},{box_max_y - s.cps[1, 1]}" + f" {s.cps[2, 0] - box_min_x},{box_max_y - s.cps[2, 1]}" + f" {s.cps[3, 0] - box_min_x},{box_max_y - s.cps[3, 1]}" ) for s in bezier_elements ] @@ -1027,7 +1025,7 @@ def _approximate_curve(original_spline, tolerance): # draw path d=path_d, style=( - f"fill:none;stroke:{_rgb_2_hex(r,g,b)};stroke-opacity:{a};" + f"fill:none;stroke:{_rgb_2_hex(r, g, b)};stroke-opacity:{a};" f"stroke-width:{spline.show_options.get('lw', 0.01)};" "stroke-linecap:round" ), @@ -1048,16 +1046,16 @@ def _approximate_curve(original_spline, tolerance): bezier_elements += spline_copy.extract.beziers() path_d = ( - f"M {bezier_elements[0].cps[0,0]-box_min_x}," - f"{box_max_y - bezier_elements[0].cps[0,1]}" + f"M {bezier_elements[0].cps[0, 0] - box_min_x}," + f"{box_max_y - bezier_elements[0].cps[0, 1]}" ) path_d += " ".join( [ ( f" C" - f" {s.cps[1,0]-box_min_x},{box_max_y - s.cps[1,1]}" - f" {s.cps[2,0]-box_min_x},{box_max_y - s.cps[2,1]}" - f" {s.cps[3,0]-box_min_x},{box_max_y - s.cps[3,1]}" + f" {s.cps[1, 0] - box_min_x},{box_max_y - s.cps[1, 1]}" + f" {s.cps[2, 0] - box_min_x},{box_max_y - s.cps[2, 1]}" + f" {s.cps[3, 0] - box_min_x},{box_max_y - s.cps[3, 1]}" ) for s in bezier_elements ] @@ -1069,8 +1067,8 @@ def _approximate_curve(original_spline, tolerance): # draw path d=path_d, style=( - f"fill:{_rgb_2_hex(r,g,b)};fill-opacity:{a};stroke:none;" - f"stroke-linecap:{kwargs.get('linecap','round')}" + f"fill:{_rgb_2_hex(r, g, b)};fill-opacity:{a};stroke:none;" + f"stroke-linecap:{kwargs.get('linecap', 'round')}" ), ) @@ -1094,7 +1092,6 @@ def _approximate_curve(original_spline, tolerance): ukvs = spline.unique_knots[0] knot_projected = spline.evaluate(ukvs.reshape(-1, 1)) for x, y in knot_projected: - _ET.SubElement( svg_knots, "rect", @@ -1103,13 +1100,11 @@ def _approximate_curve(original_spline, tolerance): height=str(lw), width=str(lw), style=( - f"fill:{_rgb_2_hex(r,g,b)};stroke:none;" - f"fill-opacity:{a};" + f"fill:{_rgb_2_hex(r, g, b)};stroke:none;fill-opacity:{a};" ), ) else: - # Extract knot lines as splines knot_lines = [] for knot in spline.unique_knots[0]: @@ -1123,16 +1118,16 @@ def _approximate_curve(original_spline, tolerance): spline_copy = _approximate_curve(knot_line, tolerance) bezier_elements = spline_copy.extract.beziers() path_d = ( - f"M {bezier_elements[0].cps[0,0]-box_min_x}," - f"{box_max_y - bezier_elements[0].cps[0,1]}" + f"M {bezier_elements[0].cps[0, 0] - box_min_x}," + f"{box_max_y - bezier_elements[0].cps[0, 1]}" ) path_d += " ".join( [ ( - f" C {s.cps[1,0]-box_min_x}," - f"{box_max_y - s.cps[1,1]}" - f" {s.cps[2,0]-box_min_x},{box_max_y - s.cps[2,1]}" - f" {s.cps[3,0]-box_min_x},{box_max_y - s.cps[3,1]}" + f" C {s.cps[1, 0] - box_min_x}," + f"{box_max_y - s.cps[1, 1]}" + f" {s.cps[2, 0] - box_min_x},{box_max_y - s.cps[2, 1]}" + f" {s.cps[3, 0] - box_min_x},{box_max_y - s.cps[3, 1]}" ) for s in bezier_elements ] @@ -1143,10 +1138,10 @@ def _approximate_curve(original_spline, tolerance): # draw path d=path_d, style=( - f"fill:none;stroke:{_rgb_2_hex(r,g,b)};" + f"fill:none;stroke:{_rgb_2_hex(r, g, b)};" f"stroke-opacity:{a};" f"stroke-width:{lw};" - f"stroke-linecap:{kwargs.get('linecap','round')}" + f"stroke-linecap:{kwargs.get('linecap', 'round')}" ), ) diff --git a/splinepy/microstructure/microstructure.py b/splinepy/microstructure/microstructure.py index 366935651..ec3424750 100644 --- a/splinepy/microstructure/microstructure.py +++ b/splinepy/microstructure/microstructure.py @@ -9,7 +9,7 @@ class Microstructure(_SplinepyBase): - """Helper class to facilitatae the construction of microstructures.""" + """Helper class to facilitate the construction of microstructures.""" def __init__( self, @@ -18,7 +18,7 @@ def __init__( microtile=None, parametrization_function=None, ): - """Helper class to facilitatae the construction of microstructures. + """Helper class to facilitate the construction of microstructures. Parameters ---------- @@ -79,8 +79,7 @@ def deformation_function(self, deformation_function): if not isinstance(deformation_function, _PySpline): raise ValueError( - "Deformation function must be splinepy-Spline." - " e.g. splinepy.NURBS" + "Deformation function must be splinepy-Spline. e.g. splinepy.NURBS" ) self._deformation_function = deformation_function @@ -120,9 +119,7 @@ def tiling(self, tiling): None """ if not isinstance(tiling, list) and not isinstance(tiling, int): - raise ValueError( - "Tiling mus be either list of integers of integer " "value" - ) + raise ValueError("Tiling mus be either list of integers of integer value") self._tiling = tiling # Is defaulted to False using function arguments self._sanity_check() @@ -263,7 +260,7 @@ def _additional_knots(self, knot_span_wise): # Create Spline that will be used to iterate over parametric space ukvs = self.deformation_function.unique_knots if knot_span_wise: - for tt, ukv in zip(self.tiling, ukvs): + for tt, ukv in zip(self.tiling, ukvs, strict=True): inv_t = 1 / tt new_knots = [ ukv[i - 1] + j * inv_t * (ukv[i] - ukv[i - 1]) @@ -276,7 +273,7 @@ def _additional_knots(self, knot_span_wise): "New knots will be inserted one by one with the objective" " to evenly distribute tiles within the parametric domain" ) - for i_pd, (tt, ukv) in enumerate(zip(self.tiling, ukvs)): + for i_pd, (tt, ukv) in enumerate(zip(self.tiling, ukvs, strict=True)): n_current_spans = len(ukv) - 1 if tt == n_current_spans: continue @@ -358,10 +355,8 @@ def _compute_tiling_prerequisites( # First step : insert all required knots into the deformation function # spline if macro_sensitivity: - knot_insertion_matrix = ( - deformation_function_copy.knot_insertion_matrix( - 0, additional_knots[0] - ) + knot_insertion_matrix = deformation_function_copy.knot_insertion_matrix( + 0, additional_knots[0] ) deformation_function_copy.insert_knots(0, additional_knots[0]) for i_pd, akv in enumerate(additional_knots[1:], start=1): @@ -427,8 +422,8 @@ def _compute_tiling_prerequisites( def create( self, closing_face=None, - knot_span_wise=None, - macro_sensitivities=None, + knot_span_wise=True, + macro_sensitivities=False, **kwargs, ): """Create a Microstructure. @@ -455,18 +450,15 @@ def create( if not self._sanity_check(): raise ValueError("Not enough information provided, abort") - # Set default values - if knot_span_wise is None: - knot_span_wise = True - if macro_sensitivities is None: - macro_sensitivities = False + if isinstance(knot_span_wise, bool) is False: + raise TypeError("knot_span_wise must be a bool") + if isinstance(macro_sensitivities, bool) is False: + raise TypeError("macro_senstivities must be a bool") # check if user wants closed structure if closing_face is not None: if not hasattr(self.microtile, "_closing_tile"): - raise ValueError( - "Microtile does not provide closing tile definition" - ) + raise ValueError("Microtile does not provide closing tile definition") closing_face_dim = {"x": 0, "y": 1, "z": 2}.get(closing_face) if closing_face is None: raise ValueError( @@ -485,9 +477,7 @@ def create( # Check if parametrized is_parametrized = self.parametrization_function is not None - parameter_sensitivities = ( - self.parameter_sensitivity_function is not None - ) + parameter_sensitivities = self.parameter_sensitivity_function is not None if parameter_sensitivities and not is_parametrized: raise ValueError( @@ -532,10 +522,7 @@ def create( # Prepare field for derivatives if macro_sensitivities or parameter_sensitivities: spline_list_derivs = [ - [] - for i in range( - n_parameter_sensitivities + n_macro_sensitivities - ) + [] for _ in range(n_parameter_sensitivities + n_macro_sensitivities) ] spline_list_ms = [] @@ -557,9 +544,7 @@ def create( # If the sensitivities are requested, evaluate the sensitivity # function, which must be provided by the user if parameter_sensitivities: - tile_sensitivities = self.parameter_sensitivity_function( - positions - ) + tile_sensitivities = self.parameter_sensitivity_function(positions) # To avoid unnecessary constructions (if a parameter # sensitivity evaluates to zero), perform only those that are # in the support of a specific design variable @@ -604,9 +589,7 @@ def create( tile_patch, compute_sensitivities=True ) spline_list_ms.append(composed) - basis_function_compositions.append( - basis_function_composition - ) + basis_function_compositions.append(basis_function_composition) else: for tile_patch in tile: spline_list_ms.append(def_fun.compose(tile_patch)) @@ -618,7 +601,7 @@ def create( for j in anti_support: spline_list_derivs[j].extend(empty_splines) for j, deris in enumerate(derivatives): - for tile_v, tile_deriv in zip(tile, deris): + for tile_v, tile_deriv in zip(tile, deris, strict=True): spline_list_derivs[support[j]].append( def_fun.composition_derivative(tile_v, tile_deriv) ) @@ -642,16 +625,10 @@ def create( control_points = _np.zeros( (cps.shape[0], self._deformation_function.dim) ) - ii_ctps, jj_dim = divmod( - j_cc, self._deformation_function.dim - ) + ii_ctps, jj_dim = divmod(j_cc, self._deformation_function.dim) control_points[:, jj_dim] = mapped_cps[:, ii_ctps] - spline_list_derivs[ - n_parameter_sensitivities + j_cc - ].append( - type(patch_info[0])( - patch_info[0].degrees, control_points - ) + spline_list_derivs[n_parameter_sensitivities + j_cc].append( + type(patch_info[0])(patch_info[0].degrees, control_points) ) # Use a multipatch object to bundle all information @@ -723,8 +700,7 @@ def _sanity_check(self): or (self.tiling is None) ): self._logd( - "Current information not sufficient," - " awaiting further assignments" + "Current information not sufficient, awaiting further assignments" ) return False # Check if microtile object fulfils requirements @@ -768,9 +744,7 @@ def _sanity_check(self): "attribute `evaluation_points`, that is required for" " a parametrized microstructure construction" ) - result = self._parametrization_function( - self._microtile.evaluation_points - ) + result = self._parametrization_function(self._microtile.evaluation_points) if not isinstance(result, _np.ndarray): raise ValueError( "Function outline of parametrization function must be " @@ -781,9 +755,7 @@ def _sanity_check(self): return True # Check sensitivity function - result = self.parameter_sensitivity_function( - self._microtile.evaluation_points - ) + result = self.parameter_sensitivity_function(self._microtile.evaluation_points) if (not isinstance(result, _np.ndarray)) or (not result.ndim == 3): raise ValueError( "Function outline of parameter sensitivity function must " @@ -823,8 +795,7 @@ def __init__(self, microtile): for m in microtile: if not isinstance(m, _PySpline): raise ValueError( - "Microtiles must be (list of) " - "splinepy-Splines. e.g. splinepy.NURBS" + "Microtiles must be (list of) splinepy-Splines. e.g. splinepy.NURBS" ) # Extract beziers for every non Bezier patch else this just # returns itself diff --git a/splinepy/microstructure/tiles/__init__.py b/splinepy/microstructure/tiles/__init__.py index 3b35a1f5c..d301320e4 100644 --- a/splinepy/microstructure/tiles/__init__.py +++ b/splinepy/microstructure/tiles/__init__.py @@ -8,6 +8,7 @@ armadillo, chi, cross_2d, + cross_3d, cross_3d_linear, cube_void, double_lattice, @@ -42,6 +43,7 @@ "chi", "cross_2d", "cube_void", + "cross_3d", "cross_3d_linear", "double_lattice", "ellips_v_oid", @@ -86,7 +88,7 @@ def _summarize_tiles(): key = SubClass.__qualname__ # save types and sort with direction tile_types[key] = SubClass - dim = SubClass.dim + dim = SubClass._dim if dim == 1: d1[key] = SubClass elif dim == 2: @@ -123,7 +125,7 @@ def by_dim(para_dim=None, dim=None): para_dim = int(para_dim) filtered = {} for key, value in pool.items(): - if value.para_dim == para_dim: + if value._para_dim == para_dim: filtered[key] = value # overwrite pool with filtered diff --git a/splinepy/microstructure/tiles/armadillo.py b/splinepy/microstructure/tiles/armadillo.py index e7f39aa66..8e4712d99 100644 --- a/splinepy/microstructure/tiles/armadillo.py +++ b/splinepy/microstructure/tiles/armadillo.py @@ -20,11 +20,25 @@ class Armadillo(_TileBase): _dim = 3 _evaluation_points = _np.array([[0.5, 0.5, 0.5]]) _n_info_per_eval_point = 1 + _sensitivities_implemented = True + _closure_directions = [ + "x_min", + "x_max", + "y_min", + "y_max", + "z_min", + "z_max", + ] + _parameter_bounds = [[0.0, 0.5]] + _parameters_shape = (1, 1) + _default_parameter_value = 0.2 + + _CONTACT_LENGTH_BOUNDS = [0.0, 0.99] def _closing_tile( self, parameters=None, - parameter_sensitivities=None, # TODO + parameter_sensitivities=None, contact_length=0.3, closure=None, **kwargs, # noqa ARG002 @@ -58,4104 +72,5017 @@ def _closing_tile( if closure is None: raise ValueError("No closing direction given") - if not isinstance(contact_length, float): - raise ValueError("Invalid Type for radius") + self._check_custom_parameter( + contact_length, "contact length", self._CONTACT_LENGTH_BOUNDS + ) - if not ((contact_length > 0) and (contact_length < 0.99)): - raise ValueError("The length of a side must be in (0.01, 0.99)") + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) - if parameters is None: - self._logd("Setting parameters to default values (0.2)") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) + splines = [] + for i_derivative in range(n_derivatives + 1): + if i_derivative == 0: + v_wall_thickness = parameters[0, 0] + v_zero = 0.0 + v_one_half = 0.5 + v_one = 1.0 + v_half_contact_length = contact_length * 0.5 + v_inner_half_contact_length = contact_length * v_wall_thickness + else: + v_wall_thickness = parameter_sensitivities[ + 0, 0, i_derivative - 1 + ] + v_zero = 0.0 + v_one_half = 0.0 + v_one = 0.0 + v_half_contact_length = 0.0 + v_inner_half_contact_length = contact_length * v_wall_thickness + + spline_list = [] + + if closure == "x_min": + # set points: + right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] ) - * 0.2 - ) - self.check_params(parameters) + connection_front_right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) - if parameter_sensitivities is not None: - raise NotImplementedError( - "Derivatives are not implemented for this tile yet" - ) + front = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) - if not (_np.all(parameters > 0) and _np.all(parameters < 0.5)): - raise ValueError( - "The thickness of the wall must be in (0.01 and 0.49)" - ) + connection_back_left = _np.array( + [ + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_zero, + v_one, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_zero, + v_zero, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + ] + ) - v_wall_thickness = parameters[0, 0] - spline_list = [] - v_zero = 0.0 - v_one_half = 0.5 - v_one = 1.0 - v_half_contact_length = contact_length * 0.5 - v_inner_half_contact_length = contact_length * parameters[0, 0] + left = _np.array( + [ + [ + v_zero, + v_one, + v_one, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_zero, + v_one, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_one, + v_zero, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_zero, + v_zero, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - if closure == "x_min": - # set points: - right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) + connection_front_left = _np.array( + [ + [ + v_zero, + v_one, + v_one, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_one, + v_zero, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - connection_front_right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) + back = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - front = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) + connection_back_right = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] + ) - connection_back_left = _np.array( - [ - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_zero, - v_one, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_zero, - v_zero, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - ] - ) + bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - left = _np.array( - [ - [ - v_zero, - v_one, - v_one, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_zero, - v_one, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_one, - v_zero, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_zero, - v_zero, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) + top = _np.array( + [ + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + ] + ) - connection_front_left = _np.array( - [ - [ - v_zero, - v_one, - v_one, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_one, - v_zero, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) + connection_front_bottom = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - back = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) + connection_front_top = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + ] + ) - connection_back_right = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) + connection_back_bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + connection_back_top = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - top = _np.array( - [ - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - ] - ) + connection_top_right = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - connection_front_bottom = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + connection_top_left = _np.array( + [ + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_zero, + v_one, + v_one, + ], + [ + v_zero, + v_zero, + v_one, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - connection_front_top = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - ] - ) - - connection_back_bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_back_top = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_top_right = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_top_left = _np.array( - [ - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_zero, - v_one, - v_one, - ], - [ - v_zero, - v_zero, - v_one, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_bottom_left = _np.array( - [ - [ - v_zero, - v_one, - v_zero, - ], - [ - v_zero, - v_zero, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - connection_bottom_right = _np.array( - [ - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - elif closure == "x_max": - # set points: - right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_zero, - v_one, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_zero, - v_zero, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_one, - v_one, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_one, - v_zero, - ], - ] - ) - - connection_front_right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_one, - v_one, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_one, - v_zero, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) - - front = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) - - connection_back_left = _np.array( - [ - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - ] - ) - - left = _np.array( - [ - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_front_left = _np.array( - [ - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - back = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_back_right = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_zero, - v_one, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_zero, - v_zero, - ], - ] - ) - - bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - top = _np.array( - [ - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - ] - ) - - connection_front_bottom = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - connection_front_top = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - ] - ) - - connection_back_bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_back_top = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_top_right = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one, - v_one, - v_one, - ], - [ - v_one, - v_zero, - v_one, - ], - [ - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_top_left = _np.array( - [ - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_bottom_left = _np.array( - [ - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - connection_bottom_right = _np.array( - [ - [ - v_one, - v_one, - v_zero, - ], - [ - v_one, - v_zero, - v_zero, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - elif closure == "y_min": - # set points: - right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) - - connection_front_right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) - - front = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) - - connection_back_left = _np.array( - [ - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_zero, - v_one, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_zero, - v_zero, - ], - ] - ) - - left = _np.array( - [ - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_front_left = _np.array( - [ - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - back = _np.array( - [ - [ - v_one, - v_zero, - v_one, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_zero, - v_zero, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_zero, - v_one, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_zero, - v_zero, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_back_right = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_zero, - v_one, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_zero, - v_zero, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) - - bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - top = _np.array( - [ - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - ] - ) - - connection_front_bottom = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - connection_front_top = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - ] - ) - - connection_back_bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one, - v_zero, - v_zero, - ], - [ - v_zero, - v_zero, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_back_top = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one, - v_zero, - v_one, - ], - [ - v_zero, - v_zero, - v_one, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_top_right = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_top_left = _np.array( - [ - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_bottom_left = _np.array( - [ - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - connection_bottom_right = _np.array( - [ - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - elif closure == "y_max": - # set points: - # set points: - right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) - - connection_front_right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_one, - v_one, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_one, - v_zero, - ], - ] - ) - - front = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_one, - v_one, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_one, - v_zero, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_one, - v_one, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_one, - v_zero, - ], - ] - ) - - connection_back_left = _np.array( - [ - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - ] - ) - - left = _np.array( - [ - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_top_left = _np.array( - [ - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_one, - v_one, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_one, - v_zero, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - back = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) + connection_bottom_left = _np.array( + [ + [ + v_zero, + v_one, + v_zero, + ], + [ + v_zero, + v_zero, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - connection_back_right = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) + connection_bottom_right = _np.array( + [ + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + elif closure == "x_max": + # set points: + right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_zero, + v_one, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_zero, + v_zero, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_one, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_zero, + ], + ] + ) - top = _np.array( - [ - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - ] - ) + connection_front_right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_one, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_zero, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) - connection_front_bottom = _np.array( - [ - [ - v_one, - v_one, - v_zero, - ], - [ - v_zero, - v_one, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + front = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) - connection_front_top = _np.array( - [ - [ - v_one, - v_one, - v_one, - ], - [ - v_zero, - v_one, - v_one, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - ] - ) + connection_back_left = _np.array( + [ + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + ] + ) - connection_back_bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - ] - ) + left = _np.array( + [ + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - connection_back_top = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - ] - ) + connection_front_left = _np.array( + [ + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - connection_top_right = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) + back = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - connection_front_left = _np.array( - [ - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) + connection_back_right = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_zero, + v_one, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_zero, + v_zero, + ], + ] + ) + + bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + top = _np.array( + [ + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + ] + ) + + connection_front_bottom = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + connection_front_top = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + ] + ) + + connection_back_bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_back_top = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_top_right = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one, + v_one, + v_one, + ], + [ + v_one, + v_zero, + v_one, + ], + [ + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_top_left = _np.array( + [ + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_bottom_left = _np.array( + [ + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + connection_bottom_right = _np.array( + [ + [ + v_one, + v_one, + v_zero, + ], + [ + v_one, + v_zero, + v_zero, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + elif closure == "y_min": + # set points: + right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] + ) + + connection_front_right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) + + front = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) + + connection_back_left = _np.array( + [ + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_zero, + v_one, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_zero, + v_zero, + ], + ] + ) + + left = _np.array( + [ + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_front_left = _np.array( + [ + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - connection_bottom_left = _np.array( - [ - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + back = _np.array( + [ + [ + v_one, + v_zero, + v_one, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_zero, + v_zero, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_zero, + v_one, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_zero, + v_zero, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - connection_bottom_right = _np.array( - [ - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + connection_back_right = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_zero, + v_one, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_zero, + v_zero, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] + ) - elif closure == "z_max": - # set points: - right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) + bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - connection_front_right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) + top = _np.array( + [ + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + ] + ) - front = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) + connection_front_bottom = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + connection_front_top = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + ] + ) + + connection_back_bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one, + v_zero, + v_zero, + ], + [ + v_zero, + v_zero, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_back_top = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one, + v_zero, + v_one, + ], + [ + v_zero, + v_zero, + v_one, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_top_right = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_top_left = _np.array( + [ + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_bottom_left = _np.array( + [ + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + connection_bottom_right = _np.array( + [ + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + elif closure == "y_max": + # set points: + # set points: + right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] + ) + + connection_front_right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_one, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_zero, + ], + ] + ) + + front = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_one, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_zero, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_one, + v_one, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_one, + v_zero, + ], + ] + ) + + connection_back_left = _np.array( + [ + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + ] + ) + + left = _np.array( + [ + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_top_left = _np.array( + [ + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_one, + v_one, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_one, + v_zero, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + back = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_back_right = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] + ) - connection_back_left = _np.array( - [ - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - ] - ) + bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - left = _np.array( - [ - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) + top = _np.array( + [ + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + ] + ) - connection_front_left = _np.array( - [ - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) + connection_front_bottom = _np.array( + [ + [ + v_one, + v_one, + v_zero, + ], + [ + v_zero, + v_one, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - back = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) + connection_front_top = _np.array( + [ + [ + v_one, + v_one, + v_one, + ], + [ + v_zero, + v_one, + v_one, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + ] + ) - connection_back_right = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) + connection_back_bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_back_top = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_top_right = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_front_left = _np.array( + [ + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_bottom_left = _np.array( + [ + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + connection_bottom_right = _np.array( + [ + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + elif closure == "z_max": + # set points: + right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] + ) + + connection_front_right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) + + front = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) + + connection_back_left = _np.array( + [ + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + ] + ) + + left = _np.array( + [ + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_front_left = _np.array( + [ + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + back = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_back_right = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] + ) + + bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + top = _np.array( + [ + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one, + v_one, + v_one, + ], + [ + v_zero, + v_one, + v_one, + ], + [ + v_one, + v_zero, + v_one, + ], + [ + v_zero, + v_zero, + v_one, + ], + ] + ) - bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + connection_front_bottom = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - top = _np.array( - [ - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one, - v_one, - v_one, - ], - [ - v_zero, - v_one, - v_one, - ], - [ - v_one, - v_zero, - v_one, - ], - [ - v_zero, - v_zero, - v_one, - ], - ] - ) + connection_front_top = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_one, + ], + [ + v_zero, + v_one, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + ] + ) - connection_front_bottom = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + connection_back_bottom = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - connection_front_top = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_one, - v_one, - ], - [ - v_zero, - v_one, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - ] - ) + connection_back_top = _np.array( + [ + [ + v_one, + v_zero, + v_one, + ], + [ + v_zero, + v_zero, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - connection_back_bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - ] - ) + connection_top_right = _np.array( + [ + [ + v_one, + v_one, + v_one, + ], + [ + v_one, + v_zero, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_top_left = _np.array( + [ + [ + v_zero, + v_one, + v_one, + ], + [ + v_zero, + v_zero, + v_one, + ], + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) + + connection_bottom_left = _np.array( + [ + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + connection_bottom_right = _np.array( + [ + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + elif closure == "z_min": + # set points: + # set points: + right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] + ) + + connection_front_right = _np.array( + [ + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) + + front = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + ] + ) + + connection_back_left = _np.array( + [ + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + ] + ) + + left = _np.array( + [ + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_front_left = _np.array( + [ + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_zero, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + -v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + back = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + ] + ) + + connection_back_right = _np.array( + [ + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + -v_wall_thickness + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half + v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + ] + ) + + bottom = _np.array( + [ + [ + v_one, + v_one, + v_zero, + ], + [ + v_zero, + v_one, + v_zero, + ], + [ + v_one, + v_zero, + v_zero, + ], + [ + v_zero, + v_zero, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + top = _np.array( + [ + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + ] + ) + + connection_front_bottom = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half - v_half_contact_length, + ], + [ + v_one, + v_one, + v_zero, + ], + [ + v_zero, + v_one, + v_zero, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + connection_front_top = _np.array( + [ + [ + v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_one, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + ] + ) + + connection_back_bottom = _np.array( + [ + [ + v_one, + v_zero, + v_zero, + ], + [ + v_zero, + v_zero, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_zero, + v_one_half - v_half_contact_length, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + ], + ] + ) - connection_back_top = _np.array( - [ - [ - v_one, - v_zero, - v_one, - ], - [ - v_zero, - v_zero, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - ] - ) + connection_back_top = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - connection_top_right = _np.array( - [ - [ - v_one, - v_one, - v_one, - ], - [ - v_one, - v_zero, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) + connection_top_right = _np.array( + [ + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - connection_top_left = _np.array( - [ - [ - v_zero, - v_one, - v_one, - ], - [ - v_zero, - v_zero, - v_one, - ], - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) + connection_top_left = _np.array( + [ + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - connection_bottom_left = _np.array( - [ - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + connection_bottom_left = _np.array( + [ + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_zero, + v_one, + v_zero, + ], + [ + v_zero, + v_zero, + v_zero, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - connection_bottom_right = _np.array( - [ - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] + connection_bottom_right = _np.array( + [ + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one, + v_one, + v_zero, + ], + [ + v_one, + v_zero, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) + + for control_points in [ + right, + connection_front_right, + front, + connection_back_left, + left, + connection_front_left, + back, + connection_back_right, + bottom, + top, + connection_front_bottom, + connection_front_top, + connection_back_bottom, + connection_back_top, + connection_top_right, + connection_top_left, + connection_bottom_left, + connection_bottom_right, + ]: + spline_list.append( + _Bezier(degrees=[1, 1, 1], control_points=control_points) + ) + + if i_derivative == 0: + splines = spline_list.copy() + else: + derivatives.append(spline_list) + + return (splines, derivatives) + + def create_tile( + self, + parameters=None, + parameter_sensitivities=None, # TODO + contact_length=0.3, + closure=None, + **kwargs, # noqa ARG002 + ): + """Create a microtile based on the parameters that describe the wall + thicknesses. + + Thickness parameters are used to describe the inner radius of the + outward facing branches + + Parameters + ---------- + parameters : np.array + One evaluation point with one parameter is used. This parameter + describes the thickness of the wall. The parameters must be a + two-dimensional np.array, where the value must be between 0.01 + and 0.49 + parameter_sensitivities: np.ndarray + Describes the parameter sensitivities with respect to some design + variable. In case the design variables directly apply to the + parameter itself, they evaluate as delta_ij + contact_length : float + the length of the wall that contacts the other microstructure + closure : str + parametric dimension that needs to be closed, given in the form + "x_min", "x_max", etc. + + Returns + ------- + microtile_list : list(splines) + derivative_list : list / None + """ + + self._check_custom_parameter( + contact_length, "contact length", self._CONTACT_LENGTH_BOUNDS + ) + + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) + + if closure is not None: + return self._closing_tile( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + contact_length=contact_length, + closure=closure, + **kwargs, ) - elif closure == "z_min": - # set points: + splines = [] + for i_derivative in range(n_derivatives + 1): + if i_derivative == 0: + v_wall_thickness = parameters[0, 0] + v_zero = 0.0 + v_one_half = 0.5 + v_one = 1.0 + v_half_contact_length = contact_length * 0.5 + v_inner_half_contact_length = contact_length * v_wall_thickness + else: + v_wall_thickness = parameter_sensitivities[ + 0, 0, i_derivative - 1 + ] + v_zero = 0.0 + v_one_half = 0.0 + v_one = 0.0 + v_half_contact_length = 0.0 + v_inner_half_contact_length = contact_length * v_wall_thickness + + spline_list = [] + # set points: right = _np.array( [ @@ -4520,23 +5447,23 @@ def _closing_tile( bottom = _np.array( [ [ - v_one, - v_one, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, v_zero, ], [ - v_zero, - v_one, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, v_zero, ], [ - v_one, - v_zero, + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, v_zero, ], [ - v_zero, - v_zero, + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, v_zero, ], [ @@ -4620,13 +5547,13 @@ def _closing_tile( v_one_half - v_half_contact_length, ], [ - v_one, - v_one, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, v_zero, ], [ - v_zero, - v_one, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, v_zero, ], [ @@ -4699,1286 +5626,301 @@ def _closing_tile( connection_back_bottom = _np.array( [ - [ - v_one, - v_zero, - v_zero, - ], - [ - v_zero, - v_zero, - v_zero, - ], [ v_one_half + v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ v_one_half - v_half_contact_length, v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_back_top = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, ], [ v_one_half - v_half_contact_length, v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_top_right = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, ], [ - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) - - connection_top_left = _np.array( - [ - [ - v_one_half - v_half_contact_length, v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ v_zero, v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, ], - ] - ) - - connection_bottom_left = _np.array( - [ [ - v_zero, - v_one_half + v_half_contact_length, v_one_half - v_half_contact_length, - ], - [ v_zero, v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_zero, - v_one, - v_zero, - ], - [ - v_zero, - v_zero, - v_zero, ], [ - v_one_half - v_wall_thickness, v_one_half + v_inner_half_contact_length, v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, v_one_half - v_wall_thickness, ], [ v_one_half - v_inner_half_contact_length, v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - connection_bottom_right = _np.array( - [ - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, + v_one_half - v_wall_thickness, ], [ - v_wall_thickness + v_one_half, v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, v_one_half - v_inner_half_contact_length, ], [ - v_wall_thickness + v_one_half, -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, v_one_half - v_inner_half_contact_length, ], + ] + ) + + connection_back_top = _np.array( + [ [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, v_one, - v_one, - v_zero, ], [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, v_one, - v_zero, - v_zero, ], [ v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, ], [ - v_one_half + v_inner_half_contact_length, v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_bottom_left) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_bottom_right) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_back_bottom) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_front_bottom) - ) - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_front_top) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_back_top) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_top_right) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_front_left) - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=right)) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_front_right) - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=back)) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_back_left) - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=left)) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_top_left) - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=front)) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_back_right) - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=bottom)) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=top)) - - return (spline_list, None) - - def create_tile( - self, - parameters=None, - parameter_sensitivities=None, # TODO - contact_length=0.3, - closure=None, - **kwargs, # noqa ARG002 - ): - """Create a microtile based on the parameters that describe the wall - thicknesses. - - Thickness parameters are used to describe the inner radius of the - outward facing branches - - Parameters - ---------- - parameters : np.array - One evaluation point with one parameter is used. This parameter - describes the thickness of the wall. The parameters must be a - two-dimensional np.array, where the value must be between 0.01 - and 0.49 - parameter_sensitivities: np.ndarray - Describes the parameter sensitivities with respect to some design - variable. In case the design variables directly apply to the - parameter itself, they evaluate as delta_ij - contact_length : float - the length of the wall that contacts the other microstructure - closure : str - parametric dimension that needs to be closed, given in the form - "x_min", "x_max", etc. - - Returns - ------- - microtile_list : list(splines) - derivative_list : list / None - """ - - if not isinstance(contact_length, float): - raise ValueError("Invalid Type for radius") - - if not ((contact_length > 0) and (contact_length < 0.99)): - raise ValueError("The length of a side must be in (0.01, 0.99)") - - if parameters is None: - self._logd("Setting parameters to default values (0.2)") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) - ) - * 0.2 - ) - - self.check_params(parameters) - - if parameter_sensitivities is not None: - raise NotImplementedError( - "Derivatives are not implemented for this tile yet" - ) - - if not (_np.all(parameters > 0) and _np.all(parameters < 0.5)): - raise ValueError( - "The thickness of the wall must be in (0.01 and 0.49)" - ) - - if closure is not None: - return self._closing_tile( - parameters=parameters, - parameter_sensitivities=parameter_sensitivities, - contact_length=contact_length, - closure=closure, - **kwargs, - ) - - v_wall_thickness = parameters[0, 0] - v_zero = 0.0 - v_one_half = 0.5 - v_one = 1.0 - v_half_contact_length = contact_length * 0.5 - v_half_contact_length = contact_length * 0.5 - v_inner_half_contact_length = contact_length * parameters[0, 0] - - spline_list = [] - - # set points: - right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) - - connection_front_right = _np.array( - [ - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) - - front = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - ] - ) - - connection_back_left = _np.array( - [ - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - ] - ) - - left = _np.array( - [ - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_front_left = _np.array( - [ - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_zero, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - -v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - back = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_back_right = _np.array( - [ - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - -v_wall_thickness + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half + v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - ] - ) - - bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - top = _np.array( - [ - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - ] - ) - - connection_front_bottom = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - connection_front_top = _np.array( - [ - [ - v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_one, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - ] - ) - - connection_back_bottom = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_zero, - v_one_half - v_half_contact_length, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - ], - ] - ) - - connection_back_top = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - -v_half_contact_length + v_one_half, - v_zero, - v_one_half + v_half_contact_length, - ], - [ - v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - [ - -v_inner_half_contact_length + v_one_half, - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - ], - ] - ) + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + -v_half_contact_length + v_one_half, + v_zero, + v_one_half + v_half_contact_length, + ], + [ + v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + [ + -v_inner_half_contact_length + v_one_half, + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - connection_top_right = _np.array( - [ - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half + v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], + connection_top_right = _np.array( [ - v_one_half + v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half + v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - connection_top_left = _np.array( - [ - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_one, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_one, - ], - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half + v_wall_thickness, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], + connection_top_left = _np.array( [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - ], - ] - ) + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_one, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_one, + ], + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half + v_wall_thickness, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + ], + ] + ) - connection_bottom_left = _np.array( - [ - [ - v_zero, - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_zero, - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - ], - [ - v_one_half - v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half - v_wall_thickness, - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_wall_thickness, - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half - v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], + connection_bottom_left = _np.array( [ - v_one_half - v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) + [ + v_zero, + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_zero, + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + ], + [ + v_one_half - v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half - v_wall_thickness, + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_wall_thickness, + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half - v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - connection_bottom_right = _np.array( - [ - [ - v_one, - v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_one, - -v_half_contact_length + v_one_half, - v_one_half - v_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_wall_thickness + v_one_half, - -v_inner_half_contact_length + v_one_half, - v_one_half - v_inner_half_contact_length, - ], - [ - v_one_half + v_half_contact_length, - v_one_half + v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_half_contact_length, - v_one_half - v_half_contact_length, - v_zero, - ], - [ - v_one_half + v_inner_half_contact_length, - v_one_half + v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], + connection_bottom_right = _np.array( [ - v_one_half + v_inner_half_contact_length, - v_one_half - v_inner_half_contact_length, - v_one_half - v_wall_thickness, - ], - ] - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=right)) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_front_right) - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=back)) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_back_left) - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=left)) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_front_left) - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=front)) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_back_right) - ) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=bottom)) - - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=top)) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_front_top) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_front_bottom) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_back_bottom) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_back_top) - ) - - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_top_right) - ) + [ + v_one, + v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_one, + -v_half_contact_length + v_one_half, + v_one_half - v_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_wall_thickness + v_one_half, + -v_inner_half_contact_length + v_one_half, + v_one_half - v_inner_half_contact_length, + ], + [ + v_one_half + v_half_contact_length, + v_one_half + v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_half_contact_length, + v_one_half - v_half_contact_length, + v_zero, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half + v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + [ + v_one_half + v_inner_half_contact_length, + v_one_half - v_inner_half_contact_length, + v_one_half - v_wall_thickness, + ], + ] + ) - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_top_left) - ) + for control_points in [ + right, + connection_front_right, + front, + connection_back_left, + left, + connection_front_left, + back, + connection_back_right, + bottom, + top, + connection_front_bottom, + connection_front_top, + connection_back_bottom, + connection_back_top, + connection_top_right, + connection_top_left, + connection_bottom_left, + connection_bottom_right, + ]: + spline_list.append( + _Bezier(degrees=[1, 1, 1], control_points=control_points) + ) - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_bottom_left) - ) - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=connection_bottom_right) - ) + if i_derivative == 0: + splines = spline_list.copy() + else: + derivatives.append(spline_list) - return (spline_list, None) + return (splines, derivatives) diff --git a/splinepy/microstructure/tiles/chi.py b/splinepy/microstructure/tiles/chi.py index b8430d0dc..4a48132dd 100644 --- a/splinepy/microstructure/tiles/chi.py +++ b/splinepy/microstructure/tiles/chi.py @@ -16,8 +16,12 @@ class Chi(_TileBase): _dim = 2 _para_dim = 1 - _evaluation_points = _np.array([[0.5, 0.5]]) + _evaluation_points = _np.array([[0.5]]) _n_info_per_eval_point = 1 + _sensitivities_implemented = True + _parameter_bounds = [[-_np.pi / 2, _np.pi / 2]] + _parameters_shape = (1, 1) + _default_parameter_value = _np.pi / 8 def create_tile( self, @@ -41,28 +45,10 @@ def create_tile( microtile_list : list(splines) """ - # set to default if nothing is given - if parameters is None: - self._logd( - "Tile request is not parametrized, setting default Pi/8" - ) - parameters = _np.array([[_np.pi / 8]]) - else: - if not ( - _np.all(parameters >= -_np.pi * 0.5) - and _np.all(parameters < _np.pi * 0.5) - ): - raise ValueError("The parameter must be in -Pi/2 and Pi/2") - pass - self.check_params(parameters) - - # Check if user requests derivative splines - if self.check_param_derivatives(parameter_sensitivities): - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) splines = [] for i_derivative in range(n_derivatives + 1): @@ -75,12 +61,13 @@ def create_tile( s = r * _np.sin(alpha) c = r * _np.cos(alpha) else: - alpha = parameter_sensitivities[0, 0, i_derivative - 1] + alpha = parameters[0, 0] + _np.pi / 4 + dalpha = float(parameter_sensitivities[0, 0, i_derivative - 1]) v_one_half = 0.0 v_zero = 0.0 r = _np.sqrt(0.125) - s = r * _np.cos(alpha) - c = -r * _np.sin(alpha) + s = dalpha * r * _np.cos(alpha) + c = dalpha * -r * _np.sin(alpha) # Init return value spline_list = [] diff --git a/splinepy/microstructure/tiles/cross_2d.py b/splinepy/microstructure/tiles/cross_2d.py index 083d0c65a..990db61f2 100644 --- a/splinepy/microstructure/tiles/cross_2d.py +++ b/splinepy/microstructure/tiles/cross_2d.py @@ -26,6 +26,23 @@ class Cross2D(_TileBase): ] ) _n_info_per_eval_point = 1 + _sensitivities_implemented = True + _closure_directions = ["x_min", "x_max", "y_min", "y_max"] + _parameters_shape = (4, 1) + _default_parameter_value = 0.2 + + # Default value of center_expansion + _center_expansion = 1.0 + + # Dynamical computation of parameter bounds depending on center expansion + @property + def _parameter_bounds(self): + max_radius = min(0.5, (0.5 / self._center_expansion)) + return [[0.0, max_radius]] * 4 + + _BOUNDARY_WIDTH_BOUNDS = [0.0, 0.5] + _FILLING_HEIGHT_BOUNDS = [0.0, 1.0] + _CENTER_EXPANSION_BOUNDS = [0.5, 1.5] def _closing_tile( self, @@ -66,36 +83,17 @@ def _closing_tile( if closure is None: raise ValueError("No closing direction given") - if parameters is None: - self._logd("Tile request is not parametrized, setting default 0.2") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) - ) - * 0.2 - ) - - self.check_params(parameters) - - if not (_np.all(parameters > 0) and _np.all(parameters < 0.5)): - raise ValueError("Thickness out of range (0, .5)") - - if not (0.0 < float(boundary_width) < 0.5): - raise ValueError("Boundary Width is out of range") - - if not (0.0 < float(filling_height) < 1.0): - raise ValueError("Filling must be in (0,1)") + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) - # Check if user requests derivative splines - if parameter_sensitivities is not None: - # Check format - self.check_param_derivatives(parameter_sensitivities) - - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None + self._check_custom_parameter( + boundary_width, "boundary width", self._BOUNDARY_WIDTH_BOUNDS + ) + self._check_custom_parameter( + filling_height, "filling height", self._FILLING_HEIGHT_BOUNDS + ) splines = [] for i_derivative in range(n_derivatives + 1): @@ -414,41 +412,16 @@ def create_tile( derivative_list : list / None """ - if not isinstance(center_expansion, float): - raise ValueError("Invalid Type") - - if not ((center_expansion > 0.5) and (center_expansion < 1.5)): - raise ValueError("Center Expansion must be in (.5,1.5)") - - max_radius = min(0.5, (0.5 / center_expansion)) - - # set to default if nothing is given - if parameters is None: - self._logd("Setting branch thickness to default 0.2") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) - ) - * 0.2 - ) - - self.check_params(parameters) - - if not (_np.all(parameters > 0) and _np.all(parameters < max_radius)): - raise ValueError(f"Thickness out of range (0, {max_radius})") - - self.check_param_derivatives(parameter_sensitivities) + self._check_custom_parameter( + center_expansion, "center expansion", self._CENTER_EXPANSION_BOUNDS + ) - # Check if user requests derivative splines - if parameter_sensitivities is not None: - # Check format - self.check_param_derivatives(parameter_sensitivities) + self._center_expansion = center_expansion - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) # Closure requested, pass to function if closure is not None: diff --git a/splinepy/microstructure/tiles/cross_3d.py b/splinepy/microstructure/tiles/cross_3d.py index 8752c9881..95fe347b0 100644 --- a/splinepy/microstructure/tiles/cross_3d.py +++ b/splinepy/microstructure/tiles/cross_3d.py @@ -28,6 +28,23 @@ class Cross3D(_TileBase): ] ) _n_info_per_eval_point = 1 + _sensitivities_implemented = True + _closure_directions = ["z_min", "z_max"] + _parameters_shape = (6, 1) + _default_parameter_value = 0.2 + + # Default value of center_expansion + _center_expansion = 1.0 + + # Dynamical computation of parameter bounds depending on center expansion + @property + def _parameter_bounds(self): + max_radius = min(0.5, (0.5 / self._center_expansion)) + return [[0.0, max_radius]] * 6 + + _BOUNDARY_WIDTH_BOUNDS = [0.0, 0.5] + _FILLING_HEIGHT_BOUNDS = [0.0, 1.0] + _CENTER_EXPANSION_BOUNDS = [0.5, 1.5] def _closing_tile( self, @@ -68,32 +85,17 @@ def _closing_tile( if closure is None: raise ValueError("No closing direction given") - if parameters is None: - self._logd("Tile request is not parametrized, setting default 0.2") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) - ) - * 0.2 - ) + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) - if not (_np.all(parameters > 0) and _np.all(parameters < 0.5)): - raise ValueError("Thickness out of range (0, .5)") - - # Check if user requests derivative splines - if parameter_sensitivities is not None: - self.check_param_derivatives(parameter_sensitivities) - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None - - if not (0.0 < float(boundary_width) < 0.5): - raise ValueError("Boundary Width is out of range") - - if not (0.0 < float(filling_height) < 1.0): - raise ValueError("Filling must be in (0,1)") + self._check_custom_parameter( + boundary_width, "boundary width", self._BOUNDARY_WIDTH_BOUNDS + ) + self._check_custom_parameter( + filling_height, "filling height", self._FILLING_HEIGHT_BOUNDS + ) splines = [] for i_derivative in range(n_derivatives + 1): @@ -518,45 +520,23 @@ def create_tile( derivative_list : list / None """ - if not isinstance(center_expansion, float): - raise ValueError("Invalid Type") + self._check_custom_parameter( + center_expansion, "center expansion", self._CENTER_EXPANSION_BOUNDS + ) - if not ((center_expansion > 0.5) and (center_expansion < 1.5)): - raise ValueError("Center Expansion must be in (.5,1.5)") + self._center_expansion = center_expansion - # Max radius, so there is no tanglement in the crosstile - max_radius = min(0.5, (0.5 / center_expansion)) - - # set to default if nothing is given - if parameters is None: - self._logd("Setting branch thickness to default 0.2") - parameters = ( - _np.ones( - ( - self._evaluation_points.shape[0], - self._n_info_per_eval_point, - ) - ) - * 0.2 - ) - - # Check for type and consistency - self.check_params(parameters) - if _np.any(parameters <= 0) or _np.any(parameters > max_radius): - raise ValueError( - f"Radii must be in (0,{max_radius}) for " - f"center_expansion {center_expansion}" - ) - - if parameter_sensitivities is not None: - self.check_param_derivatives(parameter_sensitivities) - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) if closure is not None: + if closure not in self._closure_directions: + raise NotImplementedError( + f"Closure '{closure}' not implemented. Supported closures are: " + + f"{self._closure_directions}" + ) return self._closing_tile( parameters=parameters, parameter_sensitivities=parameter_sensitivities, diff --git a/splinepy/microstructure/tiles/cross_3d_linear.py b/splinepy/microstructure/tiles/cross_3d_linear.py index 3e5901371..9f23404b4 100644 --- a/splinepy/microstructure/tiles/cross_3d_linear.py +++ b/splinepy/microstructure/tiles/cross_3d_linear.py @@ -28,6 +28,23 @@ class Cross3DLinear(_TileBase): ] ) _n_info_per_eval_point = 1 + _sensitivities_implemented = True + _closure_directions = ["z_min", "z_max"] + _parameters_shape = (6, 1) + _default_parameter_value = 0.2 + + # Default value of center_expansion + _center_expansion = 1.0 + + # Dynamical computation of parameter bounds depending on center expansion + @property + def _parameter_bounds(self): + max_radius = min(0.5, (0.5 / self._center_expansion)) + return [[0.0, max_radius]] * 6 + + _BOUNDARY_WIDTH_BOUNDS = [0.0, 0.5] + _FILLING_HEIGHT_BOUNDS = [0.0, 1.0] + _CENTER_EXPANSION_BOUNDS = [0.5, 1.5] def _closing_tile( self, @@ -46,7 +63,7 @@ def _closing_tile( Six evaluation points with one parameter is used. This parameter describes the radius of the cylinder at the evaluation point. The parameters must be a two-dimensional np.array, where the - value must be between 0.01 and 0.49 + value must be between 0.0 and 0.5 (not inclusive) parameter_sensitivities: np.ndarray(6, 1, para_dim) Describes the parameter sensitivities with respect to some design variable. In case the design variables directly apply to the @@ -68,32 +85,17 @@ def _closing_tile( if closure is None: raise ValueError("No closing direction given") - if parameters is None: - self._logd("Tile request is not parametrized, setting default 0.2") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) - ) - * 0.2 - ) + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) - if not (_np.all(parameters > 0) and _np.all(parameters < 0.5)): - raise ValueError("Thickness out of range (0, .5)") - - # Check if user requests derivative splines - if parameter_sensitivities is not None: - self.check_param_derivatives(parameter_sensitivities) - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None - - if not (0.0 < float(boundary_width) < 0.5): - raise ValueError("Boundary Width is out of range") - - if not (0.0 < float(filling_height) < 1.0): - raise ValueError("Filling must be in (0,1)") + self._check_custom_parameter( + boundary_width, "boundary width", self._BOUNDARY_WIDTH_BOUNDS + ) + self._check_custom_parameter( + filling_height, "filling height", self._FILLING_HEIGHT_BOUNDS + ) splines = [] for i_derivative in range(n_derivatives + 1): @@ -199,8 +201,7 @@ def _closing_tile( degrees=[1, 1, 1], control_points=( _np.maximum( - center_ctps - - _np.array([center_width, v_zero, v_zero]), + center_ctps - _np.array([center_width, v_zero, v_zero]), v_zero, ) if i_derivative == 0 @@ -214,8 +215,7 @@ def _closing_tile( degrees=[1, 1, 1], control_points=( _np.maximum( - center_ctps - - _np.array([v_zero, center_width, v_zero]), + center_ctps - _np.array([v_zero, center_width, v_zero]), v_zero, ) if i_derivative == 0 @@ -229,8 +229,7 @@ def _closing_tile( degrees=[1, 1, 1], control_points=( _np.minimum( - center_ctps - + _np.array([center_width, v_zero, v_zero]), + center_ctps + _np.array([center_width, v_zero, v_zero]), v_one, ) if i_derivative == 0 @@ -244,8 +243,7 @@ def _closing_tile( degrees=[1, 1, 1], control_points=( _np.minimum( - center_ctps - + _np.array([v_zero, center_width, v_zero]), + center_ctps + _np.array([v_zero, center_width, v_zero]), v_one, ) if i_derivative == 0 @@ -353,8 +351,7 @@ def _closing_tile( degrees=[1, 1, 1], control_points=( _np.maximum( - center_ctps - - _np.array([center_width, v_zero, v_zero]), + center_ctps - _np.array([center_width, v_zero, v_zero]), v_zero, ) if i_derivative == 0 @@ -368,8 +365,7 @@ def _closing_tile( degrees=[1, 1, 1], control_points=( _np.maximum( - center_ctps - - _np.array([v_zero, center_width, v_zero]), + center_ctps - _np.array([v_zero, center_width, v_zero]), v_zero, ) if i_derivative == 0 @@ -383,8 +379,7 @@ def _closing_tile( degrees=[1, 1, 1], control_points=( _np.minimum( - center_ctps - + _np.array([center_width, v_zero, v_zero]), + center_ctps + _np.array([center_width, v_zero, v_zero]), v_one, ) if i_derivative == 0 @@ -398,8 +393,7 @@ def _closing_tile( degrees=[1, 1, 1], control_points=( _np.minimum( - center_ctps - + _np.array([v_zero, center_width, v_zero]), + center_ctps + _np.array([v_zero, center_width, v_zero]), v_one, ) if i_derivative == 0 @@ -474,43 +468,16 @@ def create_tile( derivative_list : list / None """ - if not isinstance(center_expansion, float): - raise ValueError("Invalid Type") + self._check_custom_parameter( + center_expansion, "center expansion", self._CENTER_EXPANSION_BOUNDS + ) - if not ((center_expansion > 0.5) and (center_expansion < 1.5)): - raise ValueError("Center Expansion must be in (.5,1.5)") + self._center_expansion = center_expansion - # Max radius, so there is no tanglement in the crosstile - max_radius = min(0.5, (0.5 / center_expansion)) - - # set to default if nothing is given - if parameters is None: - self._logd("Setting branch thickness to default 0.2") - parameters = ( - _np.ones( - ( - self._evaluation_points.shape[0], - self._n_info_per_eval_point, - ) - ) - * 0.2 - ) - - # Check for type and consistency - self.check_params(parameters) - if _np.any(parameters <= 0) or _np.any(parameters > max_radius): - raise ValueError( - f"Radii must be in (0,{max_radius}) for " - f"center_expansion {center_expansion}" - ) - - if parameter_sensitivities is not None: - self.check_param_derivatives(parameter_sensitivities) - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) if closure is not None: return self._closing_tile( @@ -524,13 +491,12 @@ def create_tile( for i_derivative in range(n_derivatives + 1): # Constant auxiliary values if i_derivative == 0: - [x_min_r, x_max_r, y_min_r, y_max_r, z_min_r, z_max_r] = ( - parameters[:, 0] - ) + [x_min_r, x_max_r, y_min_r, y_max_r, z_min_r, z_max_r] = parameters[ + :, 0 + ] v_one_half = 0.5 center_r = center_expansion * _np.mean(parameters[:, 0]) else: - [x_min_r, x_max_r, y_min_r, y_max_r, z_min_r, z_max_r] = ( parameter_sensitivities[:, :, i_derivative - 1].flatten() ) @@ -555,9 +521,7 @@ def create_tile( ] ) spline_list.append( - _Bezier( - degrees=[1, 1, 1], control_points=center_points + center - ) + _Bezier(degrees=[1, 1, 1], control_points=center_points + center) ) # X-Axis branches diff --git a/splinepy/microstructure/tiles/cube_void.py b/splinepy/microstructure/tiles/cube_void.py index be4f1ab3a..0ac29e965 100644 --- a/splinepy/microstructure/tiles/cube_void.py +++ b/splinepy/microstructure/tiles/cube_void.py @@ -25,6 +25,17 @@ class CubeVoid(_TileBase): _para_dim = 3 _evaluation_points = _np.array([[0.5, 0.5, 0.5]]) _n_info_per_eval_point = 4 + _sensitivities_implemented = True + # TODO: clever parameter bounds and checks if given parametrization would + # still lie in unit cube + _parameter_bounds = [ + [0.0, 1.0], + [0.0, 1.0], + [-_np.pi / 2, _np.pi / 2], + [-_np.pi / 2, _np.pi / 2], + ] + _parameters_shape = (1, 4) + _default_parameter_value = _np.array([[0.5, 0.5, 0.0, 0.0]]) # Aux values _sphere_ctps = _np.array( @@ -54,7 +65,7 @@ def _rotation_matrix_y(self, angle): def _rotation_matrix_y_deriv(self, angle): cc, ss = _np.cos(angle), _np.sin(angle) - return _np.array([[-ss, 0, -cc], [0, 1, 0], [cc, 0, -ss]]) + return _np.array([[-ss, 0, -cc], [0, 0, 0], [cc, 0, -ss]]) def create_tile( self, @@ -90,21 +101,16 @@ def create_tile( microtile_list : list(splines) derivatives : list> / None """ # set to default if nothing is given - if parameters is None: - parameters = _np.array([0.5, 0.5, 0, 0]).reshape(1, 1, 4) + + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) # Create center ellipsoid # RotY * RotX * DIAG(r_x, r_yz) * T_base [r_x, r_yz, rot_x, rot_y] = parameters.flatten() - # Check if user requests derivative splines - if self.check_param_derivatives(parameter_sensitivities): - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None - # Prepare auxiliary matrices and values diag = _np.diag([r_x, r_yz, r_yz]) rotMx = self._rotation_matrix_x(rot_x) diff --git a/splinepy/microstructure/tiles/double_lattice.py b/splinepy/microstructure/tiles/double_lattice.py index ad8f6e803..eda6cd5d8 100644 --- a/splinepy/microstructure/tiles/double_lattice.py +++ b/splinepy/microstructure/tiles/double_lattice.py @@ -20,6 +20,12 @@ class DoubleLattice(_TileBase): _para_dim = 2 _evaluation_points = _np.array([[0.5, 0.5]]) _n_info_per_eval_point = 2 + _sensitivities_implemented = True + _parameter_bounds = [[0.0, 1 / (2 * (1 + _np.sqrt(2)))]] * 2 + _parameters_shape = (1, 2) + _default_parameter_value = 0.1 + + _CONTACT_LENGTH_BOUNDS = [0.0, 1.0] def create_tile( self, @@ -44,38 +50,20 @@ def create_tile( correlates with thickness of branches and entouring wall contact_length : double required for conformity between tiles, sets the length of the center - block on the tiles boundary + block on the tile's boundary Returns ------- microtile_list : list(splines) """ - if not isinstance(contact_length, float): - raise ValueError("Invalid Type") - if not ((contact_length > 0.0) and (contact_length < 1.0)): - raise ValueError("Contact length must be in (0.,1.)") - - # set to default if nothing is given - if parameters is None: - self._logd("Tile request is not parametrized, setting default 0.2") - parameters = _np.ones((1, 2)) * 0.1 - if not ( - _np.all(parameters > 0) - and _np.all(parameters < 0.5 / (1 + _np.sqrt(2))) - ): - raise ValueError( - "Parameters must be between 0.01 and 0.5/(1+sqrt(2))=0.207" - ) - - self.check_params(parameters) - - # Check if user requests derivative splines - if self.check_param_derivatives(parameter_sensitivities): - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None + self._check_custom_parameter( + contact_length, "contact length", self._CONTACT_LENGTH_BOUNDS + ) + + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) splines = [] for i_derivative in range(n_derivatives + 1): diff --git a/splinepy/microstructure/tiles/ellips_v_oid.py b/splinepy/microstructure/tiles/ellips_v_oid.py index 4b6e7bf23..37d12c669 100644 --- a/splinepy/microstructure/tiles/ellips_v_oid.py +++ b/splinepy/microstructure/tiles/ellips_v_oid.py @@ -7,6 +7,9 @@ class EllipsVoid(_TileBase): """Void in form of an ellipsoid set into a unit cell. + TODO: Currently this tile is skipped for testing, since the control points easily + lie outside of the unit cube, even though the tile itself lies within it + The Ellips(v)oid :D Parametrization acts on the elipsoid's orientation as well as on its @@ -27,6 +30,18 @@ class EllipsVoid(_TileBase): _para_dim = 3 _evaluation_points = _np.array([[0.5, 0.5, 0.5]]) _n_info_per_eval_point = 4 + _sensitivities_implemented = True + # TODO: clever parameter bounds and checks if given parametrization would + # still lie in unit cube + # Due to ellipsoid, control points very easily lie outside unit cube + _parameter_bounds = [ + [0.0, 1.0], + [0.0, 1.0], + [-_np.pi / 2, _np.pi / 2], + [-_np.pi / 2, _np.pi / 2], + ] + _parameters_shape = (1, 4) + _default_parameter_value = _np.array([[0.5, 0.5, 0, 0]]) # Aux values _c0 = 0.5 / 3**0.5 @@ -80,7 +95,7 @@ def _rotation_matrix_y(self, angle): def _rotation_matrix_y_deriv(self, angle): cc, ss = _np.cos(angle), _np.sin(angle) - return _np.array([[-ss, 0, -cc], [0, 1, 0], [cc, 0, -ss]]) + return _np.array([[-ss, 0, -cc], [0, 0, 0], [cc, 0, -ss]]) def create_tile( self, @@ -116,21 +131,15 @@ def create_tile( microtile_list : list(splines) derivatives: list> / None """ # set to default if nothing is given - if parameters is None: - parameters = _np.array([0.5, 0.5, 0, 0]).reshape(1, 1, 4) + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) # Create center ellipsoid # RotY * RotX * DIAG(r_x, r_yz) * T_base [r_x, r_yz, rot_x, rot_y] = parameters.flatten() - # Check if user requests derivative splines - if self.check_param_derivatives(parameter_sensitivities): - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None - # Prepare auxiliary matrices and values diag = _np.diag([r_x, r_yz, r_yz]) rotMx = self._rotation_matrix_x(rot_x) diff --git a/splinepy/microstructure/tiles/hollow_cube.py b/splinepy/microstructure/tiles/hollow_cube.py index fa8c15795..222124009 100644 --- a/splinepy/microstructure/tiles/hollow_cube.py +++ b/splinepy/microstructure/tiles/hollow_cube.py @@ -28,6 +28,10 @@ class HollowCube(_TileBase): ] ) _n_info_per_eval_point = 1 + _sensitivities_implemented = True + _parameter_bounds = [[0.0, 0.5]] * 7 + _parameters_shape = (7, 1) + _default_parameter_value = 0.2 def create_tile( self, @@ -53,27 +57,10 @@ def create_tile( ------- microtile_list : list(splines) """ - - if parameters is None: - self._logd("Setting parameters to default value (0.2)") - parameters = ( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) - ) - * 0.2 - ) - - self.check_params(parameters) - - if not (_np.all(parameters > 0.0) and _np.all(parameters < 0.5)): - raise ValueError("The wall thickness must be in (0.0 and 0.5)") - - if self.check_param_derivatives(parameter_sensitivities): - n_derivatives = parameter_sensitivities.shape[2] - derivatives = [] - else: - n_derivatives = 0 - derivatives = None + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) splines = [] diff --git a/splinepy/microstructure/tiles/hollow_octagon.py b/splinepy/microstructure/tiles/hollow_octagon.py index 20a628e17..e50fe53c6 100644 --- a/splinepy/microstructure/tiles/hollow_octagon.py +++ b/splinepy/microstructure/tiles/hollow_octagon.py @@ -18,11 +18,18 @@ class HollowOctagon(_TileBase): _para_dim = 2 _evaluation_points = _np.array([[0.5, 0.5]]) _n_info_per_eval_point = 1 + _sensitivities_implemented = True + _closure_directions = ["x_min", "x_max", "y_min", "y_max"] + _parameter_bounds = [[0.0, 0.5]] + _parameters_shape = (1, 1) + _default_parameter_value = 0.2 + + _CONTACT_LENGTH_BOUNDS = [0.0, 0.99] def _closing_tile( self, parameters=None, - parameter_sensitivities=None, # TODO + parameter_sensitivities=None, contact_length=0.2, closure=None, ): @@ -50,263 +57,426 @@ def _closing_tile( """ if closure is None: raise ValueError("No closing direction given") + if isinstance(closure, int): + closure = self._closure_directions[closure] + if closure not in self._closure_directions: + raise ValueError( + f"Closure direction {closure} not implemented. " + f"Choose from {self._closure_directions}" + ) + + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) + + v_h_void = parameters[0, 0] - if parameters is None: - self._log("Tile request is not parametrized, setting default 0.2") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) + splines = [] + for i_derivative in range(n_derivatives + 1): + if i_derivative == 0: + v_zero = 0.0 + v_one_half = 0.5 + v_one = 1.0 + v_outer_c_h = contact_length * 0.5 + v_inner_c_h = contact_length * v_h_void + else: + v_zero = 0.0 + v_one_half = 0.0 + v_one = 0.0 + v_h_void = parameter_sensitivities[0, 0, i_derivative - 1] + v_outer_c_h = 0.0 + v_inner_c_h = contact_length * v_h_void + + spline_list = [] + + if closure == "x_min": + # set points: + right = _np.array( + [ + [v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_one, -v_outer_c_h + v_one_half], + [v_h_void + v_one_half, v_inner_c_h + v_one_half], + [v_one, v_outer_c_h + v_one_half], + ] ) - * 0.2 - ) - if not (_np.all(parameters > 0) and _np.all(parameters < 0.5)): - raise ValueError( - "The thickness of the wall must be in (0.01 and 0.49)" - ) + right_top = _np.array( + [ + [v_h_void + v_one_half, v_inner_c_h + v_one_half], + [v_one, v_outer_c_h + v_one_half], + [v_inner_c_h + v_one_half, v_h_void + v_one_half], + [v_outer_c_h + v_one_half, v_one], + ] + ) - self.check_params(parameters) + top = _np.array( + [ + [v_inner_c_h + v_one_half, v_h_void + v_one_half], + [v_outer_c_h + v_one_half, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half], + [-v_outer_c_h + v_one_half, v_one], + ] + ) - if parameter_sensitivities is not None: - raise NotImplementedError( - "Derivatives are not implemented for this tile yet" - ) + bottom_left = _np.array( + [ + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_zero, v_zero], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [-v_outer_c_h + v_one_half, v_zero], + ] + ) - v_h_void = parameters[0, 0] - if not ((v_h_void > 0.01) and (v_h_void < 0.5)): - raise ValueError( - "The thickness of the wall must be in (0.01 and 0.49)" - ) + left = _np.array( + [ + [v_zero, v_zero], + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_zero, v_one], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half], + ] + ) - spline_list = [] - v_zero = 0.0 - v_one_half = 0.5 - v_one = 1.0 - v_outer_c_h = contact_length * 0.5 - v_inner_c_h = contact_length * parameters[0, 0] + top_left = _np.array( + [ + [v_zero, v_one], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half], + [-v_outer_c_h + v_one_half, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half], + ] + ) - if closure == "x_min": - # set points: - right = _np.array( - [ - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - ] - ) + bottom = _np.array( + [ + [v_outer_c_h + v_one_half, v_zero], + [v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [-v_outer_c_h + v_one_half, v_zero], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], + ] + ) - right_top = _np.array( - [ - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - ] - ) + bottom_right = _np.array( + [ + [v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [v_outer_c_h + v_one_half, v_zero], + [v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_one, -v_outer_c_h + v_one_half], + ] + ) - top = _np.array( - [ - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - ] - ) + elif closure == "x_max": + right = _np.array( + [ + [v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_one, v_zero], + [v_h_void + v_one_half, v_inner_c_h + v_one_half], + [v_one, v_one], + ] + ) - bottom_left = _np.array( - [ - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - ] - ) + right_top = _np.array( + [ + [v_h_void + v_one_half, v_inner_c_h + v_one_half], + [v_one, v_one], + [v_inner_c_h + v_one_half, v_h_void + v_one_half], + [v_outer_c_h + v_one_half, v_one], + ] + ) - left = _np.array( - [ - [v_zero, v_zero], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_one], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - ] - ) + top = _np.array( + [ + [v_inner_c_h + v_one_half, v_h_void + v_one_half], + [v_outer_c_h + v_one_half, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half], + [-v_outer_c_h + v_one_half, v_one], + ] + ) - top_left = _np.array( - [ - [v_zero, v_one], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - ] - ) + bottom_left = _np.array( + [ + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_zero, -v_outer_c_h + v_one_half], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [-v_outer_c_h + v_one_half, v_zero], + ] + ) - bottom = _np.array( - [ - [v_outer_c_h + v_one_half, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - ] - ) + left = _np.array( + [ + [v_zero, -v_outer_c_h + v_one_half], + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_zero, v_outer_c_h + v_one_half], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half], + ] + ) - bottom_right = _np.array( - [ - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_zero], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - ] - ) + top_left = _np.array( + [ + [v_zero, v_outer_c_h + v_one_half], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half], + [-v_outer_c_h + v_one_half, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half], + ] + ) - elif closure == "x_max": - right = _np.array( - [ - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, v_zero], - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_one], - ] - ) + bottom = _np.array( + [ + [v_outer_c_h + v_one_half, v_zero], + [v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [-v_outer_c_h + v_one_half, v_zero], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], + ] + ) - right_top = _np.array( - [ - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_one], - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - ] - ) + bottom_right = _np.array( + [ + [v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [v_outer_c_h + v_one_half, v_zero], + [v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_one, v_zero], + ] + ) - top = _np.array( - [ - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - ] - ) + elif closure == "y_min": + # set points: + right = _np.array( + [ + [v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_one, -v_outer_c_h + v_one_half], + [v_h_void + v_one_half, v_inner_c_h + v_one_half], + [v_one, v_outer_c_h + v_one_half], + ] + ) - bottom_left = _np.array( - [ - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, -v_outer_c_h + v_one_half], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - ] - ) + right_top = _np.array( + [ + [v_h_void + v_one_half, v_inner_c_h + v_one_half], + [v_one, v_outer_c_h + v_one_half], + [v_inner_c_h + v_one_half, v_h_void + v_one_half], + [v_outer_c_h + v_one_half, v_one], + ] + ) - left = _np.array( - [ - [v_zero, -v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - ] - ) + top = _np.array( + [ + [v_inner_c_h + v_one_half, v_h_void + v_one_half], + [v_outer_c_h + v_one_half, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half], + [-v_outer_c_h + v_one_half, v_one], + ] + ) - top_left = _np.array( - [ - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - ] - ) + bottom_left = _np.array( + [ + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_zero, -v_outer_c_h + v_one_half], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [v_zero, v_zero], + ] + ) - bottom = _np.array( - [ - [v_outer_c_h + v_one_half, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - ] - ) + left = _np.array( + [ + [v_zero, -v_outer_c_h + v_one_half], + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_zero, v_outer_c_h + v_one_half], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half], + ] + ) - bottom_right = _np.array( - [ - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_zero], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, v_zero], - ] - ) + top_left = _np.array( + [ + [v_zero, v_outer_c_h + v_one_half], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half], + [-v_outer_c_h + v_one_half, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half], + ] + ) - elif closure == "y_min": - # set points: - right = _np.array( - [ - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - ] - ) + bottom = _np.array( + [ + [v_one, v_zero], + [v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [v_zero, v_zero], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], + ] + ) - right_top = _np.array( - [ - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - ] - ) + bottom_right = _np.array( + [ + [v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [v_one, v_zero], + [v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_one, -v_outer_c_h + v_one_half], + ] + ) - top = _np.array( - [ - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - ] - ) + elif closure == "y_max": + # set points: + right = _np.array( + [ + [v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_one, -v_outer_c_h + v_one_half], + [v_h_void + v_one_half, v_inner_c_h + v_one_half], + [v_one, v_outer_c_h + v_one_half], + ] + ) - bottom_left = _np.array( - [ - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, -v_outer_c_h + v_one_half], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_zero, v_zero], - ] - ) + right_top = _np.array( + [ + [v_h_void + v_one_half, v_inner_c_h + v_one_half], + [v_one, v_outer_c_h + v_one_half], + [v_inner_c_h + v_one_half, v_h_void + v_one_half], + [v_one, v_one], + ] + ) - left = _np.array( - [ - [v_zero, -v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - ] - ) + top = _np.array( + [ + [v_inner_c_h + v_one_half, v_h_void + v_one_half], + [v_one, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half], + [v_zero, v_one], + ] + ) - top_left = _np.array( - [ - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - ] - ) + bottom_left = _np.array( + [ + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_zero, -v_outer_c_h + v_one_half], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [-v_outer_c_h + v_one_half, v_zero], + ] + ) - bottom = _np.array( - [ - [v_one, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_zero, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - ] - ) + left = _np.array( + [ + [v_zero, -v_outer_c_h + v_one_half], + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_zero, v_outer_c_h + v_one_half], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half], + ] + ) - bottom_right = _np.array( - [ - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_one, v_zero], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - ] + top_left = _np.array( + [ + [v_zero, v_outer_c_h + v_one_half], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half], + [v_zero, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half], + ] + ) + + bottom = _np.array( + [ + [v_outer_c_h + v_one_half, v_zero], + [v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [-v_outer_c_h + v_one_half, v_zero], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], + ] + ) + + bottom_right = _np.array( + [ + [v_inner_c_h + v_one_half, -v_h_void + v_one_half], + [v_outer_c_h + v_one_half, v_zero], + [v_h_void + v_one_half, -v_inner_c_h + v_one_half], + [v_one, -v_outer_c_h + v_one_half], + ] + ) + + for control_points in [ + right, + right_top, + bottom, + bottom_left, + left, + top_left, + top, + bottom_right, + ]: + spline_list.append( + _Bezier(degrees=[1, 1], control_points=control_points) + ) + + if i_derivative == 0: + splines = spline_list.copy() + else: + derivatives.append(spline_list) + + return (splines, derivatives) + + def create_tile( + self, + parameters=None, + parameter_sensitivities=None, + contact_length=0.2, + closure=None, + **kwargs, # noqa ARG002 + ): + """Create a microtile based on the parameters that describe the wall + thicknesses. + + Thickness parameters are used to describe the inner radius of the + outward facing branches + + Parameters + ---------- + parameters : np.array(1, 1) + One evaluation point with one parameter is used. This parameter + specifies the distance from the center to the inner edge, where + the value must be between non-inclusive (0, 0.5). + parameter_sensitivities: np.ndarray + Describes the parameter sensitivities with respect to some design + variable. In case the design variables directly apply to the + parameter itself, they evaluate as delta_ij + contact_length : float + the length of the wall that contacts the other microstructure + closure : str + parametric dimension that needs to be closed, given in the form + "x_min", "x_max", etc. + + Returns + ------- + microtile_list : list(splines) + derivatives: list> / None + """ + self._check_custom_parameter( + contact_length, "contact length", self._CONTACT_LENGTH_BOUNDS + ) + + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) + + if closure is not None: + return self._closing_tile( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + contact_length=contact_length, + closure=closure, ) - elif closure == "y_max": + splines = [] + for i_derivative in range(n_derivatives + 1): + if i_derivative == 0: + v_zero = 0.0 + v_one_half = 0.5 + v_one = 1.0 + v_h_void = parameters[0, 0] + v_outer_c_h = contact_length * 0.5 + v_inner_c_h = contact_length * v_h_void + else: + v_zero = 0.0 + v_one_half = 0.0 + v_one = 0.0 + v_h_void = parameter_sensitivities[0, 0, i_derivative - 1] + v_outer_c_h = 0.0 + v_inner_c_h = contact_length * v_h_void + + spline_list = [] + # set points: right = _np.array( [ @@ -322,16 +492,16 @@ def _closing_tile( [v_h_void + v_one_half, v_inner_c_h + v_one_half], [v_one, v_outer_c_h + v_one_half], [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_one, v_one], + [v_outer_c_h + v_one_half, v_one], ] ) top = _np.array( [ [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_one, v_one], + [v_outer_c_h + v_one_half, v_one], [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_zero, v_one], + [-v_outer_c_h + v_one_half, v_one], ] ) @@ -357,7 +527,7 @@ def _closing_tile( [ [v_zero, v_outer_c_h + v_one_half], [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_zero, v_one], + [-v_outer_c_h + v_one_half, v_one], [-v_inner_c_h + v_one_half, v_h_void + v_one_half], ] ) @@ -380,195 +550,23 @@ def _closing_tile( ] ) - spline_list.append(_Bezier(degrees=[1, 1], control_points=right)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=right_top)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=bottom)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=bottom_left)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=left)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=top_left)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=top)) - - spline_list.append( - _Bezier(degrees=[1, 1], control_points=bottom_right) - ) - - return (spline_list, None) - - def create_tile( - self, - parameters=None, - parameter_sensitivities=None, # TODO - contact_length=0.2, - closure=None, - **kwargs, # noqa ARG002 - ): - """Create a microtile based on the parameters that describe the wall - thicknesses. - - Thickness parameters are used to describe the inner radius of the - outward facing branches - - Parameters - ---------- - parameters : np.array(1, 1) - One evaluation point with one parameter is used. This parameter - specifies the distance from the center to the inner edge, where - the value must be between non-inclusive (0, 0.5). - parameter_sensitivities: np.ndarray - Describes the parameter sensitivities with respect to some design - variable. In case the design variables directly apply to the - parameter itself, they evaluate as delta_ij - contact_length : float - the length of the wall that contacts the other microstructure - closure : str - parametric dimension that needs to be closed, given in the form - "x_min", "x_max", etc. - - Returns - ------- - microtile_list : list(splines) - derivatives: list> / None - """ - - if not isinstance(contact_length, float): - raise ValueError("Invalid Type for radius") - - if not ((contact_length > 0) and (contact_length < 0.99)): - raise ValueError("The length of a side must be in (0.01, 0.99)") - - if parameters is None: - self._logd("Setting parameters to default values (0.2)") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) + for control_points in [ + right, + right_top, + bottom, + bottom_left, + left, + top_left, + top, + bottom_right, + ]: + spline_list.append( + _Bezier(degrees=[1, 1], control_points=control_points) ) - * 0.2 - ) - - if parameter_sensitivities is not None: - raise NotImplementedError( - "Derivatives are not implemented for this tile yet" - ) - - self.check_params(parameters) - - v_h_void = parameters[0, 0] - if not (_np.all(parameters > 0) and _np.all(parameters < 0.5)): - raise ValueError( - "The thickness of the wall must be in (0.01 and 0.49)" - ) - - if closure is not None: - return self._closing_tile( - parameters=parameters, - parameter_sensitivities=parameter_sensitivities, # TODO - contact_length=contact_length, - closure=closure, - ) - - v_zero = 0.0 - v_one_half = 0.5 - v_one = 1.0 - v_outer_c_h = contact_length * 0.5 - v_inner_c_h = contact_length * parameters[0, 0] - - spline_list = [] - - # set points: - right = _np.array( - [ - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - ] - ) - right_top = _np.array( - [ - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - ] - ) - - top = _np.array( - [ - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - ] - ) - - bottom_left = _np.array( - [ - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, -v_outer_c_h + v_one_half], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - ] - ) - - left = _np.array( - [ - [v_zero, -v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - ] - ) - - top_left = _np.array( - [ - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - ] - ) - - bottom = _np.array( - [ - [v_outer_c_h + v_one_half, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - ] - ) - - bottom_right = _np.array( - [ - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_zero], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - ] - ) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=right)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=right_top)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=bottom)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=bottom_left)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=left)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=top_left)) - - spline_list.append(_Bezier(degrees=[1, 1], control_points=top)) - - spline_list.append( - _Bezier(degrees=[1, 1], control_points=bottom_right) - ) + if i_derivative == 0: + splines = spline_list.copy() + else: + derivatives.append(spline_list) - return (spline_list, None) + return (splines, derivatives) diff --git a/splinepy/microstructure/tiles/hollow_octagon_extrude.py b/splinepy/microstructure/tiles/hollow_octagon_extrude.py index 82e738d23..a49bfc3cb 100644 --- a/splinepy/microstructure/tiles/hollow_octagon_extrude.py +++ b/splinepy/microstructure/tiles/hollow_octagon_extrude.py @@ -18,12 +18,20 @@ class HollowOctagonExtrude(_TileBase): _para_dim = 3 _evaluation_points = _np.array([[0.5, 0.5, 0.5]]) _n_info_per_eval_point = 1 + _sensitivities_implemented = True + _closure_directions = ["x_min", "x_max", "y_min", "y_max"] + _parameter_bounds = [[0.0, 0.5]] + _parameters_shape = (1, 1) + _default_parameter_value = 0.2 + + _CONTACT_LENGTH_BOUNDS = [0.0, 0.99] def create_tile( self, parameters=None, - parameter_sensitivities=None, # TODO + parameter_sensitivities=None, contact_length=0.2, + closure=None, **kwargs, # noqa ARG002 ): """Create a microtile based on the parameters that describe the wall @@ -50,177 +58,193 @@ def create_tile( derivatives: list> / None """ - if not isinstance(contact_length, float): - raise ValueError("Invalid Type for radius") - - if not ((contact_length > 0) and (contact_length < 0.99)): - raise ValueError("The length of a side must be in (0.01, 0.99)") - - if parameters is None: - self._logd("Setting parameters to default values (0.2)") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) - ) - * 0.2 - ) - - if parameter_sensitivities is not None: - raise NotImplementedError( - "Derivatives are not implemented for this tile yet" - ) - - self.check_params(parameters) - - v_h_void = parameters[0, 0] - if not ((v_h_void > 0.01) and (v_h_void < 0.5)): - raise ValueError( - "The thickness of the wall must be in (0.01 and 0.49)" - ) - - v_zero = 0.0 - v_one_half = 0.5 - v_one = 1.0 - v_outer_c_h = contact_length * 0.5 - v_inner_c_h = contact_length * parameters[0, 0] - - spline_list = [] - - # set points: - right = _np.array( - [ - [v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_zero], - [v_one, -v_outer_c_h + v_one_half, 0.0], - [v_h_void + v_one_half, v_inner_c_h + v_one_half, 0.0], - [v_one, v_outer_c_h + v_one_half, 0.0], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_one], - [v_one, -v_outer_c_h + v_one_half, v_one], - [v_h_void + v_one_half, v_inner_c_h + v_one_half, v_one], - [v_one, v_outer_c_h + v_one_half, v_one], - ] + self._check_custom_parameter( + contact_length, "contact length", self._CONTACT_LENGTH_BOUNDS ) - - right_top = _np.array( - [ - [v_h_void + v_one_half, v_inner_c_h + v_one_half, v_zero], - [v_one, v_outer_c_h + v_one_half, v_zero], - [v_inner_c_h + v_one_half, v_h_void + v_one_half, v_zero], - [v_outer_c_h + v_one_half, v_one, v_zero], - [v_h_void + v_one_half, v_inner_c_h + v_one_half, v_one], - [v_one, v_outer_c_h + v_one_half, v_one], - [v_inner_c_h + v_one_half, v_h_void + v_one_half, v_one], - [v_outer_c_h + v_one_half, v_one, v_one], - ] + # Process input + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, ) - top = _np.array( - [ - [v_inner_c_h + v_one_half, v_h_void + v_one_half, v_zero], - [v_outer_c_h + v_one_half, v_one, v_zero], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half, v_zero], - [-v_outer_c_h + v_one_half, v_one, v_zero], - [v_inner_c_h + v_one_half, v_h_void + v_one_half, v_one], - [v_outer_c_h + v_one_half, v_one, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half, v_one], - [-v_outer_c_h + v_one_half, v_one, v_one], - ] - ) - - bottom_left = _np.array( - [ - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_zero], - [v_zero, -v_outer_c_h + v_one_half, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_zero], - [-v_outer_c_h + v_one_half, v_zero, v_zero], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_one], - [v_zero, -v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_one], - [-v_outer_c_h + v_one_half, v_zero, v_one], - ] - ) + v_h_void = parameters[0, 0] - left = _np.array( - [ - [v_zero, -v_outer_c_h + v_one_half, v_zero], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_zero], - [v_zero, v_outer_c_h + v_one_half, v_zero], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half, v_zero], - [v_zero, -v_outer_c_h + v_one_half, v_one], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_one], - [v_zero, v_outer_c_h + v_one_half, v_one], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half, v_one], - ] - ) + if closure is not None: + return self._closing_tile( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + closure=closure, + contact_length=contact_length, + **kwargs, + ) - top_left = _np.array( - [ - [v_zero, v_outer_c_h + v_one_half, v_zero], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half, v_zero], - [-v_outer_c_h + v_one_half, v_one, v_zero], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half, v_zero], - [v_zero, v_outer_c_h + v_one_half, v_one], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half, v_one], - [-v_outer_c_h + v_one_half, v_one, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half, v_one], - ] - ) + splines = [] + for i_derivative in range(n_derivatives + 1): + if i_derivative == 0: + v_zero = 0.0 + v_one_half = 0.5 + v_one = 1.0 + v_outer_c_h = contact_length * 0.5 + v_inner_c_h = contact_length * v_h_void + else: + v_zero = 0.0 + v_one_half = 0.0 + v_one = 0.0 + v_h_void = parameter_sensitivities[0, 0, i_derivative - 1] + v_outer_c_h = 0.0 + v_inner_c_h = contact_length * v_h_void + + spline_list = [] - bottom = _np.array( - [ - [v_outer_c_h + v_one_half, v_zero, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_zero], - [-v_outer_c_h + v_one_half, v_zero, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_zero], - [v_outer_c_h + v_one_half, v_zero, v_one], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_one], - [-v_outer_c_h + v_one_half, v_zero, v_one], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_one], - ] - ) + # set points: + right = _np.array( + [ + [v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_zero], + [v_one, -v_outer_c_h + v_one_half, v_zero], + [v_h_void + v_one_half, v_inner_c_h + v_one_half, v_zero], + [v_one, v_outer_c_h + v_one_half, v_zero], + [v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_one], + [v_one, -v_outer_c_h + v_one_half, v_one], + [v_h_void + v_one_half, v_inner_c_h + v_one_half, v_one], + [v_one, v_outer_c_h + v_one_half, v_one], + ] + ) - bottom_right = _np.array( - [ - [v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_zero], - [v_outer_c_h + v_one_half, v_zero, v_zero], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_zero], - [v_one, -v_outer_c_h + v_one_half, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_one], - [v_outer_c_h + v_one_half, v_zero, v_one], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_one], - [v_one, -v_outer_c_h + v_one_half, v_one], - ] - ) + right_top = _np.array( + [ + [v_h_void + v_one_half, v_inner_c_h + v_one_half, v_zero], + [v_one, v_outer_c_h + v_one_half, v_zero], + [v_inner_c_h + v_one_half, v_h_void + v_one_half, v_zero], + [v_outer_c_h + v_one_half, v_one, v_zero], + [v_h_void + v_one_half, v_inner_c_h + v_one_half, v_one], + [v_one, v_outer_c_h + v_one_half, v_one], + [v_inner_c_h + v_one_half, v_h_void + v_one_half, v_one], + [v_outer_c_h + v_one_half, v_one, v_one], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=right)) + top = _np.array( + [ + [v_inner_c_h + v_one_half, v_h_void + v_one_half, v_zero], + [v_outer_c_h + v_one_half, v_one, v_zero], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half, v_zero], + [-v_outer_c_h + v_one_half, v_one, v_zero], + [v_inner_c_h + v_one_half, v_h_void + v_one_half, v_one], + [v_outer_c_h + v_one_half, v_one, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half, v_one], + [-v_outer_c_h + v_one_half, v_one, v_one], + ] + ) - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=right_top) - ) + bottom_left = _np.array( + [ + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, -v_outer_c_h + v_one_half, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_zero, v_zero], + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_one], + [v_zero, -v_outer_c_h + v_one_half, v_one], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_one], + [-v_outer_c_h + v_one_half, v_zero, v_one], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=bottom)) + left = _np.array( + [ + [v_zero, -v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, v_outer_c_h + v_one_half, v_zero], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half, v_zero], + [v_zero, -v_outer_c_h + v_one_half, v_one], + [-v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_one], + [v_zero, v_outer_c_h + v_one_half, v_one], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half, v_one], + ] + ) - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=bottom_left) - ) + top_left = _np.array( + [ + [v_zero, v_outer_c_h + v_one_half, v_zero], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half, v_zero], + [-v_outer_c_h + v_one_half, v_one, v_zero], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half, v_zero], + [v_zero, v_outer_c_h + v_one_half, v_one], + [-v_h_void + v_one_half, v_inner_c_h + v_one_half, v_one], + [-v_outer_c_h + v_one_half, v_one, v_one], + [-v_inner_c_h + v_one_half, v_h_void + v_one_half, v_one], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=left)) + bottom = _np.array( + [ + [v_outer_c_h + v_one_half, v_zero, v_zero], + [v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_zero], + [-v_outer_c_h + v_one_half, v_zero, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_zero, v_one], + [v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_one], + [-v_outer_c_h + v_one_half, v_zero, v_one], + [-v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_one], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=top_left)) + bottom_right = _np.array( + [ + [v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_zero], + [v_outer_c_h + v_one_half, v_zero, v_zero], + [v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_zero], + [v_one, -v_outer_c_h + v_one_half, v_zero], + [v_inner_c_h + v_one_half, -v_h_void + v_one_half, v_one], + [v_outer_c_h + v_one_half, v_zero, v_one], + [v_h_void + v_one_half, -v_inner_c_h + v_one_half, v_one], + [v_one, -v_outer_c_h + v_one_half, v_one], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1, 1], control_points=top)) + for control_points in [ + right, + right_top, + top, + bottom_left, + left, + top_left, + bottom, + bottom_right, + ]: + spline_list.append( + _Bezier(degrees=[1, 1, 1], control_points=control_points) + ) - spline_list.append( - _Bezier(degrees=[1, 1, 1], control_points=bottom_right) - ) + if i_derivative == 0: + splines = spline_list.copy() + else: + derivatives.append(spline_list) - return (spline_list, None) + return (splines, derivatives) - def closing_tile( + def _closing_tile( self, parameters=None, - parameter_sensitivities=None, # TODO + parameter_sensitivities=None, contact_length=0.2, closure=None, + **kwargs, # noqa ARG002 ): """Create a closing tile to match with closed surface. @@ -234,7 +258,7 @@ def closing_tile( Describes the parameter sensitivities with respect to some design variable. In case the design variables directly apply to the parameter itself, they evaluate as delta_ij - closure : int + closure : int or str parametric dimension that needs to be closed. Positive values mean that minimum parametric dimension is requested. That means, i.e. -2 closes the tile at maximum z-coordinate. @@ -249,352 +273,990 @@ def closing_tile( """ if closure is None: raise ValueError("No closing direction given") - - if parameters is None: - self._log("Tile request is not parametrized, setting default 0.2") - parameters = _np.array( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) - ) - * 0.2 - ) - - if not (_np.all(parameters[0] > 0) and _np.all(parameters[0] < 0.5)): + if isinstance(closure, int): + closure = self._closure_directions[closure] + if closure not in self._closure_directions: raise ValueError( - "The thickness of the wall must be in (0.01 and 0.49)" + f"Closure direction {closure} not implemented. " + f"Choose from {self._closure_directions}" ) - self.check_params(parameters) - - if parameter_sensitivities is not None: - raise NotImplementedError( - "Derivatives are not implemented for this tile yet" - ) + # Process input + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) v_h_void = parameters[0, 0] - if not ((v_h_void > 0.01) and (v_h_void < 0.5)): - raise ValueError( - "The thickness of the wall must be in (0.01 and 0.49)" - ) - spline_list = [] - v_zero = 0.0 - v_one_half = 0.5 - v_one = 1.0 - v_outer_c_h = contact_length * 0.5 - v_inner_c_h = contact_length * parameters[0, 0] - - if closure == "x_min": - # set points: - right = _np.array( - [ - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - ] - ) - - right_top = _np.array( - [ - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - ] - ) - - top = _np.array( - [ - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - ] - ) - - bottom_left = _np.array( - [ - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - ] - ) - - left = _np.array( - [ - [v_zero, v_zero], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_one], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - ] - ) - - top_left = _np.array( - [ - [v_zero, v_one], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - ] - ) - - bottom = _np.array( - [ - [v_outer_c_h + v_one_half, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - ] - ) + splines = [] + for i_derivative in range(n_derivatives + 1): + if i_derivative == 0: + v_zero = 0.0 + v_one_half = 0.5 + v_one = 1.0 + v_outer_c_h = contact_length * 0.5 + v_inner_c_h = contact_length * parameters[0, 0] + else: + v_h_void = parameter_sensitivities[0, 0, i_derivative - 1] + v_zero, v_one_half, v_one = [0.0, 0.0, 0.0] + v_outer_c_h = 0.0 + v_inner_c_h = contact_length * v_h_void + + spline_list = [] + + if closure == "x_min": + # set points: + right = _np.array( + [ + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, -v_outer_c_h + v_one_half, v_zero], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_outer_c_h + v_one_half, v_zero], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_one, -v_outer_c_h + v_one_half, v_one], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_outer_c_h + v_one_half, v_one], + ] + ) - bottom_right = _np.array( - [ - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_zero], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - ] - ) + right_top = _np.array( + [ + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_outer_c_h + v_one_half, v_zero], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_one, v_zero], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_outer_c_h + v_one_half, v_one], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [v_outer_c_h + v_one_half, v_one, v_one], + ] + ) - elif closure == "x_max": - right = _np.array( - [ - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, v_zero], - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_one], - ] - ) + top = _np.array( + [ + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_one, v_zero], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_one, v_zero], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [v_outer_c_h + v_one_half, v_one, v_one], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_one, v_one], + ] + ) - right_top = _np.array( - [ - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_one], - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - ] - ) + bottom_left = _np.array( + [ + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, v_zero, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_zero, v_zero], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_zero, v_zero, v_one], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_zero, v_one], + ] + ) - top = _np.array( - [ - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - ] - ) + left = _np.array( + [ + [v_zero, v_zero, v_zero], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, v_one, v_zero], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, v_zero, v_one], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_zero, v_one, v_one], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + ] + ) - bottom_left = _np.array( - [ - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, -v_outer_c_h + v_one_half], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - ] - ) + top_left = _np.array( + [ + [v_zero, v_one, v_zero], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_one, v_zero], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_zero, v_one, v_one], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_one, v_one], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + ] + ) - left = _np.array( - [ - [v_zero, -v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - ] - ) + bottom = _np.array( + [ + [v_outer_c_h + v_one_half, v_zero, v_zero], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_zero, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_zero, v_one], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_zero, v_one], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + ] + ) - top_left = _np.array( - [ - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - ] - ) + bottom_right = _np.array( + [ + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_zero, v_zero], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, -v_outer_c_h + v_one_half, v_zero], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [v_outer_c_h + v_one_half, v_zero, v_one], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_one, -v_outer_c_h + v_one_half, v_one], + ] + ) - bottom = _np.array( - [ - [v_outer_c_h + v_one_half, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - ] - ) + elif closure == "x_max": + right = _np.array( + [ + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_zero, v_zero], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_one, v_zero], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_zero, v_one], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_one, v_one], + ] + ) - bottom_right = _np.array( - [ - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_zero], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, v_zero], - ] - ) + right_top = _np.array( + [ + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_one, v_zero], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_one, v_zero], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_one, v_one], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [v_outer_c_h + v_one_half, v_one, v_one], + ] + ) - elif closure == "y_min": - # set points: - right = _np.array( - [ - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - ] - ) + top = _np.array( + [ + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_one, v_zero], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_one, v_zero], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [v_outer_c_h + v_one_half, v_one, v_one], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_one, v_one], + ] + ) - right_top = _np.array( - [ - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - ] - ) + bottom_left = _np.array( + [ + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, -v_outer_c_h + v_one_half, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_zero, v_zero], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_zero, -v_outer_c_h + v_one_half, v_one], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_zero, v_one], + ] + ) - top = _np.array( - [ - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - ] - ) + left = _np.array( + [ + [v_zero, -v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, -v_outer_c_h + v_one_half, v_one], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_zero, v_outer_c_h + v_one_half, v_one], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + ] + ) - bottom_left = _np.array( - [ - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, -v_outer_c_h + v_one_half], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_zero, v_zero], - ] - ) + top_left = _np.array( + [ + [v_zero, v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_one, v_zero], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_zero, v_outer_c_h + v_one_half, v_one], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_one, v_one], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + ] + ) - left = _np.array( - [ - [v_zero, -v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - ] - ) + bottom = _np.array( + [ + [v_outer_c_h + v_one_half, v_zero, v_zero], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_zero, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_zero, v_one], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_zero, v_one], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + ] + ) - top_left = _np.array( - [ - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - [-v_outer_c_h + v_one_half, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - ] - ) + bottom_right = _np.array( + [ + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_zero, v_zero], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_zero, v_zero], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [v_outer_c_h + v_one_half, v_zero, v_one], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_zero, v_one], + ] + ) - bottom = _np.array( - [ - [v_one, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_zero, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - ] - ) + elif closure == "y_min": + # set points: + right = _np.array( + [ + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, -v_outer_c_h + v_one_half, v_zero], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_outer_c_h + v_one_half, v_zero], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_one, -v_outer_c_h + v_one_half, v_one], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_outer_c_h + v_one_half, v_one], + ] + ) - bottom_right = _np.array( - [ - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_one, v_zero], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - ] - ) + right_top = _np.array( + [ + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_outer_c_h + v_one_half, v_zero], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_one, v_zero], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_outer_c_h + v_one_half, v_one], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [v_outer_c_h + v_one_half, v_one, v_one], + ] + ) - elif closure == "y_max": - # set points: - right = _np.array( - [ - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - ] - ) + top = _np.array( + [ + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_one, v_zero], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_one, v_zero], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [v_outer_c_h + v_one_half, v_one, v_one], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_one, v_one], + ] + ) - right_top = _np.array( - [ - [v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_one, v_outer_c_h + v_one_half], - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_one, v_one], - ] - ) + bottom_left = _np.array( + [ + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, -v_outer_c_h + v_one_half, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_zero, v_zero, v_zero], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_zero, -v_outer_c_h + v_one_half, v_one], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [v_zero, v_zero, v_one], + ] + ) - top = _np.array( - [ - [v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_one, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - [v_zero, v_one], - ] - ) + left = _np.array( + [ + [v_zero, -v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, -v_outer_c_h + v_one_half, v_one], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_zero, v_outer_c_h + v_one_half, v_one], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + ] + ) - bottom_left = _np.array( - [ - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, -v_outer_c_h + v_one_half], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - ] - ) + top_left = _np.array( + [ + [v_zero, v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_one, v_zero], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_zero, v_outer_c_h + v_one_half, v_one], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_one, v_one], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + ] + ) - left = _np.array( - [ - [v_zero, -v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - ] - ) + bottom = _np.array( + [ + [v_one, v_zero, v_zero], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_zero, v_zero, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_one, v_zero, v_one], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [v_zero, v_zero, v_one], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + ] + ) - top_left = _np.array( - [ - [v_zero, v_outer_c_h + v_one_half], - [-v_h_void + v_one_half, v_inner_c_h + v_one_half], - [v_zero, v_one], - [-v_inner_c_h + v_one_half, v_h_void + v_one_half], - ] - ) + bottom_right = _np.array( + [ + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_one, v_zero, v_zero], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, -v_outer_c_h + v_one_half, v_zero], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [v_one, v_zero, v_one], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_one, -v_outer_c_h + v_one_half, v_one], + ] + ) - bottom = _np.array( - [ - [v_outer_c_h + v_one_half, v_zero], - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [-v_outer_c_h + v_one_half, v_zero], - [-v_inner_c_h + v_one_half, -v_h_void + v_one_half], - ] - ) + elif closure == "y_max": + # set points: + right = _np.array( + [ + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, -v_outer_c_h + v_one_half, v_zero], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_outer_c_h + v_one_half, v_zero], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_one, -v_outer_c_h + v_one_half, v_one], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_outer_c_h + v_one_half, v_one], + ] + ) - bottom_right = _np.array( - [ - [v_inner_c_h + v_one_half, -v_h_void + v_one_half], - [v_outer_c_h + v_one_half, v_zero], - [v_h_void + v_one_half, -v_inner_c_h + v_one_half], - [v_one, -v_outer_c_h + v_one_half], - ] - ) + right_top = _np.array( + [ + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, v_outer_c_h + v_one_half, v_zero], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_one, v_one, v_zero], + [ + v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [v_one, v_outer_c_h + v_one_half, v_one], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [v_one, v_one, v_one], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1], control_points=right)) + top = _np.array( + [ + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_one, v_one, v_zero], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_zero, v_one, v_zero], + [ + v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [v_one, v_one, v_one], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + [v_zero, v_one, v_one], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1], control_points=right_top)) + bottom_left = _np.array( + [ + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, -v_outer_c_h + v_one_half, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_zero, v_zero], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_zero, -v_outer_c_h + v_one_half, v_one], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_zero, v_one], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1], control_points=bottom)) + left = _np.array( + [ + [v_zero, -v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, -v_outer_c_h + v_one_half, v_one], + [ + -v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_zero, v_outer_c_h + v_one_half, v_one], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1], control_points=bottom_left)) + top_left = _np.array( + [ + [v_zero, v_outer_c_h + v_one_half, v_zero], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_zero, + ], + [v_zero, v_one, v_zero], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_zero, + ], + [v_zero, v_outer_c_h + v_one_half, v_one], + [ + -v_h_void + v_one_half, + v_inner_c_h + v_one_half, + v_one, + ], + [v_zero, v_one, v_one], + [ + -v_inner_c_h + v_one_half, + v_h_void + v_one_half, + v_one, + ], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1], control_points=left)) + bottom = _np.array( + [ + [v_outer_c_h + v_one_half, v_zero, v_zero], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [-v_outer_c_h + v_one_half, v_zero, v_zero], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_zero, v_one], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [-v_outer_c_h + v_one_half, v_zero, v_one], + [ + -v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1], control_points=top_left)) + bottom_right = _np.array( + [ + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_zero, + ], + [v_outer_c_h + v_one_half, v_zero, v_zero], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_zero, + ], + [v_one, -v_outer_c_h + v_one_half, v_zero], + [ + v_inner_c_h + v_one_half, + -v_h_void + v_one_half, + v_one, + ], + [v_outer_c_h + v_one_half, v_zero, v_one], + [ + v_h_void + v_one_half, + -v_inner_c_h + v_one_half, + v_one, + ], + [v_one, -v_outer_c_h + v_one_half, v_one], + ] + ) - spline_list.append(_Bezier(degrees=[1, 1], control_points=top)) + for control_points in [ + right, + right_top, + bottom, + bottom_left, + left, + top_left, + top, + bottom_right, + ]: + spline_list.append( + _Bezier(degrees=[1, 1, 1], control_points=control_points) + ) - spline_list.append( - _Bezier(degrees=[1, 1], control_points=bottom_right) - ) + if i_derivative == 0: + splines = spline_list.copy() + else: + derivatives.append(spline_list) - return (spline_list, None) + return (splines, derivatives) diff --git a/splinepy/microstructure/tiles/inverse_cross_3d.py b/splinepy/microstructure/tiles/inverse_cross_3d.py index c24954702..43e5ed632 100644 --- a/splinepy/microstructure/tiles/inverse_cross_3d.py +++ b/splinepy/microstructure/tiles/inverse_cross_3d.py @@ -32,6 +32,16 @@ class InverseCross3D(_TileBase): ] ) _n_info_per_eval_point = 1 + # TODO: implemented sensitivities are not correct + _sensitivities_implemented = True + _closure_directions = ["z_min", "z_max"] + _parameter_bounds = [[0.2, 0.3]] * 6 # For default values + _parameters_shape = (6, 1) + _default_parameter_value = 0.21 + + _BOUNDARY_WIDTH_BOUNDS = [0.0, 0.5] + _FILLING_HEIGHT_BOUNDS = [0.0, 1.0] + _CENTER_EXPANSION_BOUNDS = [0.5, 1.5] def _closing_tile( self, @@ -40,7 +50,7 @@ def _closing_tile( closure=None, boundary_width=0.1, filling_height=0.5, - seperator_distance=None, + separator_distance=0.3, **kwargs, # noqa ARG002 ): """Create a closing tile to match with closed surface. @@ -53,7 +63,7 @@ def _closing_tile( with of the boundary surrounding branch filling_height : float portion of the height that is filled in parametric domain - seperator_distance : float + separator_distance : float Describes the position of the separator layer in the control point domain closure : str @@ -69,827 +79,951 @@ def _closing_tile( if closure is None: raise ValueError("No closing direction given") - # Set default values - if seperator_distance is None: - seperator_distance = 0.3 - - if parameters is None: - self._logd("Tile request is not parametrized, setting default 0.2") - parameters = ( - _np.ones( - ( - self._evaluation_points.shape[0], - self._n_info_per_eval_point, - ) - ) - * 0.2 - ) - - self.check_params(parameters) - - if parameter_sensitivities is not None: - raise NotImplementedError( - "Derivatives are not implemented for this tile yet" - ) - - if not (_np.all(parameters > 0) and _np.all(parameters < 0.5)): - raise ValueError("Thickness out of range (0, .5)") - - if not (0.0 < float(boundary_width) < 0.5): - raise ValueError("Boundary Width is out of range") - - if not (0.0 < float(filling_height) < 1.0): - raise ValueError("Filling must be in (0,1)") + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) - # Precompute auxiliary values - inv_filling_height = 1.0 - filling_height - ctps_mid_height_top = (1 + filling_height) * 0.5 - ctps_mid_height_bottom = 1.0 - ctps_mid_height_top - center_width = 1.0 - 2 * boundary_width - r_center = center_width * 0.5 - half_r_center = (r_center + 0.5) * 0.5 - aux_column_width = 0.5 - 2 * (0.5 - seperator_distance) + self._check_custom_parameter( + boundary_width, "boundary width", self._BOUNDARY_WIDTH_BOUNDS + ) + self._check_custom_parameter( + filling_height, "filling height", self._FILLING_HEIGHT_BOUNDS + ) - spline_list = [] - if closure == "z_min": - branch_thickness = parameters.flatten()[5] - branch_neighbor_x_min_ctps = _np.array( - [ - [-0.5, -r_center, filling_height], - [-half_r_center, -r_center, filling_height], - [-r_center, -r_center, filling_height], - [-0.5, r_center, filling_height], - [-half_r_center, r_center, filling_height], - [-r_center, r_center, filling_height], - [-0.5, -aux_column_width, ctps_mid_height_top], - [ - -seperator_distance, - -aux_column_width, - ctps_mid_height_top, - ], + splines = [] + + for i_derivative in range(n_derivatives + 1): + if i_derivative == 0: + # Auxiliary values + fill_height_aux = filling_height + sep_distance_aux = separator_distance + inv_filling_height = 1.0 - filling_height + ctps_mid_height_top = (1 + filling_height) * 0.5 + ctps_mid_height_bottom = 1.0 - ctps_mid_height_top + center_width = 1.0 - 2 * boundary_width + r_center = center_width * 0.5 + half_r_center = (r_center + 0.5) * 0.5 + aux_column_width = 0.5 - 2 * (0.5 - separator_distance) + v_zero = 0.0 + v_one_half = 0.5 + v_one = 1.0 + if closure == "z_min": + branch_thickness = parameters.flatten()[5] + elif closure == "z_max": + branch_thickness = parameters.flatten()[4] + else: + fill_height_aux = 0.0 + sep_distance_aux = 0.0 + inv_filling_height = 0.0 + ctps_mid_height_top = 0.0 + ctps_mid_height_bottom = 0.0 + center_width = 0.0 + r_center = 0.0 + half_r_center = 0.0 + aux_column_width = 0.0 + v_zero, v_one_half, v_one = [0.0] * 3 + if closure == "z_min": + branch_thickness = parameter_sensitivities.flatten()[5] + elif closure == "z_max": + branch_thickness = parameter_sensitivities.flatten()[4] + spline_list = [] + if closure == "z_min": + branch_neighbor_x_min_ctps = _np.array( [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_top, - ], - [-0.5, aux_column_width, ctps_mid_height_top], - [ - -seperator_distance, - aux_column_width, - ctps_mid_height_top, - ], - [-branch_thickness, branch_thickness, ctps_mid_height_top], - [-0.5, -aux_column_width, 1.0], - [-seperator_distance, -aux_column_width, 1.0], - [-branch_thickness, -branch_thickness, 1.0], - [-0.5, aux_column_width, 1.0], - [-seperator_distance, aux_column_width, 1.0], - [-branch_thickness, branch_thickness, 1.0], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 1, 2], - control_points=branch_neighbor_x_min_ctps, + [-v_one_half, -r_center, fill_height_aux], + [-half_r_center, -r_center, fill_height_aux], + [-r_center, -r_center, fill_height_aux], + [-v_one_half, r_center, fill_height_aux], + [-half_r_center, r_center, fill_height_aux], + [-r_center, r_center, fill_height_aux], + [-v_one_half, -aux_column_width, ctps_mid_height_top], + [ + -sep_distance_aux, + -aux_column_width, + ctps_mid_height_top, + ], + [ + -branch_thickness, + -branch_thickness, + ctps_mid_height_top, + ], + [-v_one_half, aux_column_width, ctps_mid_height_top], + [ + -sep_distance_aux, + aux_column_width, + ctps_mid_height_top, + ], + [ + -branch_thickness, + branch_thickness, + ctps_mid_height_top, + ], + [-v_one_half, -aux_column_width, v_one], + [-sep_distance_aux, -aux_column_width, v_one], + [-branch_thickness, -branch_thickness, v_one], + [-v_one_half, aux_column_width, v_one], + [-sep_distance_aux, aux_column_width, v_one], + [-branch_thickness, branch_thickness, v_one], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 1, 2], + control_points=branch_neighbor_x_min_ctps, + ) ) - ) - branch_neighbor_x_max_ctps = _np.array( - [ - [r_center, -r_center, filling_height], - [half_r_center, -r_center, filling_height], - [0.5, -r_center, filling_height], - [r_center, r_center, filling_height], - [half_r_center, r_center, filling_height], - [0.5, r_center, filling_height], - [branch_thickness, -branch_thickness, ctps_mid_height_top], + branch_neighbor_x_max_ctps = _np.array( [ - seperator_distance, - -aux_column_width, - ctps_mid_height_top, - ], - [0.5, -aux_column_width, ctps_mid_height_top], - [branch_thickness, branch_thickness, ctps_mid_height_top], - [ - seperator_distance, - aux_column_width, - ctps_mid_height_top, - ], - [0.5, aux_column_width, ctps_mid_height_top], - [branch_thickness, -branch_thickness, 1.0], - [seperator_distance, -aux_column_width, 1.0], - [0.5, -aux_column_width, 1.0], - [branch_thickness, branch_thickness, 1.0], - [seperator_distance, aux_column_width, 1.0], - [0.5, aux_column_width, 1.0], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 1, 2], - control_points=branch_neighbor_x_max_ctps, + [r_center, -r_center, fill_height_aux], + [half_r_center, -r_center, fill_height_aux], + [v_one_half, -r_center, fill_height_aux], + [r_center, r_center, fill_height_aux], + [half_r_center, r_center, fill_height_aux], + [v_one_half, r_center, fill_height_aux], + [ + branch_thickness, + -branch_thickness, + ctps_mid_height_top, + ], + [ + sep_distance_aux, + -aux_column_width, + ctps_mid_height_top, + ], + [v_one_half, -aux_column_width, ctps_mid_height_top], + [ + branch_thickness, + branch_thickness, + ctps_mid_height_top, + ], + [ + sep_distance_aux, + aux_column_width, + ctps_mid_height_top, + ], + [v_one_half, aux_column_width, ctps_mid_height_top], + [branch_thickness, -branch_thickness, v_one], + [sep_distance_aux, -aux_column_width, v_one], + [v_one_half, -aux_column_width, v_one], + [branch_thickness, branch_thickness, v_one], + [sep_distance_aux, aux_column_width, v_one], + [v_one_half, aux_column_width, v_one], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 1, 2], + control_points=branch_neighbor_x_max_ctps, + ) ) - ) - branch_neighbor_y_min_ctps = _np.array( - [ - [-r_center, -0.5, filling_height], - [r_center, -0.5, filling_height], - [-r_center, -half_r_center, filling_height], - [r_center, -half_r_center, filling_height], - [-r_center, -r_center, filling_height], - [r_center, -r_center, filling_height], - [-aux_column_width, -0.5, ctps_mid_height_top], - [aux_column_width, -0.5, ctps_mid_height_top], + branch_neighbor_y_min_ctps = _np.array( [ - -aux_column_width, - -seperator_distance, - ctps_mid_height_top, - ], - [ - aux_column_width, - -seperator_distance, - ctps_mid_height_top, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_top, - ], - [branch_thickness, -branch_thickness, ctps_mid_height_top], - [-aux_column_width, -0.5, 1.0], - [aux_column_width, -0.5, 1.0], - [-aux_column_width, -seperator_distance, 1.0], - [aux_column_width, -seperator_distance, 1.0], - [-branch_thickness, -branch_thickness, 1.0], - [branch_thickness, -branch_thickness, 1.0], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[1, 2, 2], - control_points=branch_neighbor_y_min_ctps, + [-r_center, -v_one_half, fill_height_aux], + [r_center, -v_one_half, fill_height_aux], + [-r_center, -half_r_center, fill_height_aux], + [r_center, -half_r_center, fill_height_aux], + [-r_center, -r_center, fill_height_aux], + [r_center, -r_center, fill_height_aux], + [-aux_column_width, -v_one_half, ctps_mid_height_top], + [aux_column_width, -v_one_half, ctps_mid_height_top], + [ + -aux_column_width, + -sep_distance_aux, + ctps_mid_height_top, + ], + [ + aux_column_width, + -sep_distance_aux, + ctps_mid_height_top, + ], + [ + -branch_thickness, + -branch_thickness, + ctps_mid_height_top, + ], + [ + branch_thickness, + -branch_thickness, + ctps_mid_height_top, + ], + [-aux_column_width, -v_one_half, v_one], + [aux_column_width, -v_one_half, v_one], + [-aux_column_width, -sep_distance_aux, v_one], + [aux_column_width, -sep_distance_aux, v_one], + [-branch_thickness, -branch_thickness, v_one], + [branch_thickness, -branch_thickness, v_one], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[1, 2, 2], + control_points=branch_neighbor_y_min_ctps, + ) ) - ) - branch_neighbor_y_max_ctps = _np.array( - [ - [-r_center, r_center, filling_height], - [r_center, r_center, filling_height], - [-r_center, half_r_center, filling_height], - [r_center, half_r_center, filling_height], - [-r_center, 0.5, filling_height], - [r_center, 0.5, filling_height], - [-branch_thickness, branch_thickness, ctps_mid_height_top], - [branch_thickness, branch_thickness, ctps_mid_height_top], + branch_neighbor_y_max_ctps = _np.array( [ - -aux_column_width, - seperator_distance, - ctps_mid_height_top, - ], - [ - aux_column_width, - seperator_distance, - ctps_mid_height_top, - ], - [-aux_column_width, 0.5, ctps_mid_height_top], - [aux_column_width, 0.5, ctps_mid_height_top], - [-branch_thickness, branch_thickness, 1.0], - [branch_thickness, branch_thickness, 1.0], - [-aux_column_width, seperator_distance, 1.0], - [aux_column_width, seperator_distance, 1.0], - [-aux_column_width, 0.5, 1.0], - [aux_column_width, 0.5, 1.0], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[1, 2, 2], - control_points=branch_neighbor_y_max_ctps, + [-r_center, r_center, fill_height_aux], + [r_center, r_center, fill_height_aux], + [-r_center, half_r_center, fill_height_aux], + [r_center, half_r_center, fill_height_aux], + [-r_center, v_one_half, fill_height_aux], + [r_center, v_one_half, fill_height_aux], + [ + -branch_thickness, + branch_thickness, + ctps_mid_height_top, + ], + [ + branch_thickness, + branch_thickness, + ctps_mid_height_top, + ], + [ + -aux_column_width, + sep_distance_aux, + ctps_mid_height_top, + ], + [ + aux_column_width, + sep_distance_aux, + ctps_mid_height_top, + ], + [-aux_column_width, v_one_half, ctps_mid_height_top], + [aux_column_width, v_one_half, ctps_mid_height_top], + [-branch_thickness, branch_thickness, v_one], + [branch_thickness, branch_thickness, v_one], + [-aux_column_width, sep_distance_aux, v_one], + [aux_column_width, sep_distance_aux, v_one], + [-aux_column_width, v_one_half, v_one], + [aux_column_width, v_one_half, v_one], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[1, 2, 2], + control_points=branch_neighbor_y_max_ctps, + ) ) - ) - branch_x_min_y_min_ctps = _np.array( - [ - [-0.5, -0.5, filling_height], - [-half_r_center, -0.5, filling_height], - [-r_center, -0.5, filling_height], - [-0.5, -half_r_center, filling_height], - [-half_r_center, -half_r_center, filling_height], - [-r_center, -half_r_center, filling_height], - [-0.5, -r_center, filling_height], - [-half_r_center, -r_center, filling_height], - [-r_center, -r_center, filling_height], - [-0.5, -0.5, ctps_mid_height_top], - [-seperator_distance, -0.5, ctps_mid_height_top], - [-aux_column_width, -0.5, ctps_mid_height_top], - [-0.5, -seperator_distance, ctps_mid_height_top], - [ - -seperator_distance, - -seperator_distance, - ctps_mid_height_top, - ], - [ - -aux_column_width, - -seperator_distance, - ctps_mid_height_top, - ], - [-0.5, -aux_column_width, ctps_mid_height_top], + branch_x_min_y_min_ctps = _np.array( [ - -seperator_distance, - -aux_column_width, - ctps_mid_height_top, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_top, - ], - [-0.5, -0.5, 1.0], - [-seperator_distance, -0.5, 1.0], - [-aux_column_width, -0.5, 1.0], - [-0.5, -seperator_distance, 1.0], - [-seperator_distance, -seperator_distance, 1.0], - [-aux_column_width, -seperator_distance, 1.0], - [-0.5, -aux_column_width, 1.0], - [-seperator_distance, -aux_column_width, 1.0], - [-branch_thickness, -branch_thickness, 1.0], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 2, 2], control_points=branch_x_min_y_min_ctps + [-v_one_half, -v_one_half, fill_height_aux], + [-half_r_center, -v_one_half, fill_height_aux], + [-r_center, -v_one_half, fill_height_aux], + [-v_one_half, -half_r_center, fill_height_aux], + [-half_r_center, -half_r_center, fill_height_aux], + [-r_center, -half_r_center, fill_height_aux], + [-v_one_half, -r_center, fill_height_aux], + [-half_r_center, -r_center, fill_height_aux], + [-r_center, -r_center, fill_height_aux], + [-v_one_half, -v_one_half, ctps_mid_height_top], + [-sep_distance_aux, -v_one_half, ctps_mid_height_top], + [-aux_column_width, -v_one_half, ctps_mid_height_top], + [-v_one_half, -sep_distance_aux, ctps_mid_height_top], + [ + -sep_distance_aux, + -sep_distance_aux, + ctps_mid_height_top, + ], + [ + -aux_column_width, + -sep_distance_aux, + ctps_mid_height_top, + ], + [-v_one_half, -aux_column_width, ctps_mid_height_top], + [ + -sep_distance_aux, + -aux_column_width, + ctps_mid_height_top, + ], + [ + -branch_thickness, + -branch_thickness, + ctps_mid_height_top, + ], + [-v_one_half, -v_one_half, v_one], + [-sep_distance_aux, -v_one_half, v_one], + [-aux_column_width, -v_one_half, v_one], + [-v_one_half, -sep_distance_aux, v_one], + [-sep_distance_aux, -sep_distance_aux, v_one], + [-aux_column_width, -sep_distance_aux, v_one], + [-v_one_half, -aux_column_width, v_one], + [-sep_distance_aux, -aux_column_width, v_one], + [-branch_thickness, -branch_thickness, v_one], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 2, 2], + control_points=branch_x_min_y_min_ctps, + ) ) - ) - branch_x_min_y_max_ctps = _np.array( - [ - [-0.5, r_center, filling_height], - [-half_r_center, r_center, filling_height], - [-r_center, r_center, filling_height], - [-0.5, half_r_center, filling_height], - [-half_r_center, half_r_center, filling_height], - [-r_center, half_r_center, filling_height], - [-0.5, 0.5, filling_height], - [-half_r_center, 0.5, filling_height], - [-r_center, 0.5, filling_height], - [-0.5, aux_column_width, ctps_mid_height_top], - [ - -seperator_distance, - aux_column_width, - ctps_mid_height_top, - ], - [-branch_thickness, branch_thickness, ctps_mid_height_top], - [-0.5, seperator_distance, ctps_mid_height_top], + branch_x_min_y_max_ctps = _np.array( [ - -seperator_distance, - seperator_distance, - ctps_mid_height_top, - ], - [ - -aux_column_width, - seperator_distance, - ctps_mid_height_top, - ], - [-0.5, 0.5, ctps_mid_height_top], - [-seperator_distance, 0.5, ctps_mid_height_top], - [-aux_column_width, 0.5, ctps_mid_height_top], - [-0.5, aux_column_width, 1.0], - [-seperator_distance, aux_column_width, 1.0], - [-branch_thickness, branch_thickness, 1.0], - [-0.5, seperator_distance, 1.0], - [-seperator_distance, seperator_distance, 1.0], - [-aux_column_width, seperator_distance, 1.0], - [-0.5, 0.5, 1.0], - [-seperator_distance, 0.5, 1.0], - [-aux_column_width, 0.5, 1.0], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 2, 2], control_points=branch_x_min_y_max_ctps + [-v_one_half, r_center, fill_height_aux], + [-half_r_center, r_center, fill_height_aux], + [-r_center, r_center, fill_height_aux], + [-v_one_half, half_r_center, fill_height_aux], + [-half_r_center, half_r_center, fill_height_aux], + [-r_center, half_r_center, fill_height_aux], + [-v_one_half, v_one_half, fill_height_aux], + [-half_r_center, v_one_half, fill_height_aux], + [-r_center, v_one_half, fill_height_aux], + [-v_one_half, aux_column_width, ctps_mid_height_top], + [ + -sep_distance_aux, + aux_column_width, + ctps_mid_height_top, + ], + [ + -branch_thickness, + branch_thickness, + ctps_mid_height_top, + ], + [-v_one_half, sep_distance_aux, ctps_mid_height_top], + [ + -sep_distance_aux, + sep_distance_aux, + ctps_mid_height_top, + ], + [ + -aux_column_width, + sep_distance_aux, + ctps_mid_height_top, + ], + [-v_one_half, v_one_half, ctps_mid_height_top], + [-sep_distance_aux, v_one_half, ctps_mid_height_top], + [-aux_column_width, v_one_half, ctps_mid_height_top], + [-v_one_half, aux_column_width, v_one], + [-sep_distance_aux, aux_column_width, v_one], + [-branch_thickness, branch_thickness, v_one], + [-v_one_half, sep_distance_aux, v_one], + [-sep_distance_aux, sep_distance_aux, v_one], + [-aux_column_width, sep_distance_aux, v_one], + [-v_one_half, v_one_half, v_one], + [-sep_distance_aux, v_one_half, v_one], + [-aux_column_width, v_one_half, v_one], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 2, 2], + control_points=branch_x_min_y_max_ctps, + ) ) - ) - branch_x_max_y_min_ctps = _np.array( - [ - [r_center, -0.5, filling_height], - [half_r_center, -0.5, filling_height], - [0.5, -0.5, filling_height], - [r_center, -half_r_center, filling_height], - [half_r_center, -half_r_center, filling_height], - [0.5, -half_r_center, filling_height], - [r_center, -r_center, filling_height], - [half_r_center, -r_center, filling_height], - [0.5, -r_center, filling_height], - [aux_column_width, -0.5, ctps_mid_height_top], - [seperator_distance, -0.5, ctps_mid_height_top], - [0.5, -0.5, ctps_mid_height_top], - [ - aux_column_width, - -seperator_distance, - ctps_mid_height_top, - ], - [ - seperator_distance, - -seperator_distance, - ctps_mid_height_top, - ], - [0.5, -seperator_distance, ctps_mid_height_top], - [branch_thickness, -branch_thickness, ctps_mid_height_top], + branch_x_max_y_min_ctps = _np.array( [ - seperator_distance, - -aux_column_width, - ctps_mid_height_top, - ], - [0.5, -aux_column_width, ctps_mid_height_top], - [aux_column_width, -0.5, 1.0], - [seperator_distance, -0.5, 1.0], - [0.5, -0.5, 1.0], - [aux_column_width, -seperator_distance, 1.0], - [seperator_distance, -seperator_distance, 1.0], - [0.5, -seperator_distance, 1.0], - [branch_thickness, -branch_thickness, 1.0], - [seperator_distance, -aux_column_width, 1.0], - [0.5, -aux_column_width, 1.0], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 2, 2], control_points=branch_x_max_y_min_ctps + [r_center, -v_one_half, fill_height_aux], + [half_r_center, -v_one_half, fill_height_aux], + [v_one_half, -v_one_half, fill_height_aux], + [r_center, -half_r_center, fill_height_aux], + [half_r_center, -half_r_center, fill_height_aux], + [v_one_half, -half_r_center, fill_height_aux], + [r_center, -r_center, fill_height_aux], + [half_r_center, -r_center, fill_height_aux], + [v_one_half, -r_center, fill_height_aux], + [aux_column_width, -v_one_half, ctps_mid_height_top], + [sep_distance_aux, -v_one_half, ctps_mid_height_top], + [v_one_half, -v_one_half, ctps_mid_height_top], + [ + aux_column_width, + -sep_distance_aux, + ctps_mid_height_top, + ], + [ + sep_distance_aux, + -sep_distance_aux, + ctps_mid_height_top, + ], + [v_one_half, -sep_distance_aux, ctps_mid_height_top], + [ + branch_thickness, + -branch_thickness, + ctps_mid_height_top, + ], + [ + sep_distance_aux, + -aux_column_width, + ctps_mid_height_top, + ], + [v_one_half, -aux_column_width, ctps_mid_height_top], + [aux_column_width, -v_one_half, v_one], + [sep_distance_aux, -v_one_half, v_one], + [v_one_half, -v_one_half, v_one], + [aux_column_width, -sep_distance_aux, v_one], + [sep_distance_aux, -sep_distance_aux, v_one], + [v_one_half, -sep_distance_aux, v_one], + [branch_thickness, -branch_thickness, v_one], + [sep_distance_aux, -aux_column_width, v_one], + [v_one_half, -aux_column_width, v_one], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 2, 2], + control_points=branch_x_max_y_min_ctps, + ) ) - ) - branch_x_max_y_max_ctps = _np.array( - [ - [r_center, r_center, filling_height], - [half_r_center, r_center, filling_height], - [0.5, r_center, filling_height], - [r_center, half_r_center, filling_height], - [half_r_center, half_r_center, filling_height], - [0.5, half_r_center, filling_height], - [r_center, 0.5, filling_height], - [half_r_center, 0.5, filling_height], - [0.5, 0.5, filling_height], - [branch_thickness, branch_thickness, ctps_mid_height_top], - [ - seperator_distance, - aux_column_width, - ctps_mid_height_top, - ], - [0.5, aux_column_width, ctps_mid_height_top], - [ - aux_column_width, - seperator_distance, - ctps_mid_height_top, - ], + branch_x_max_y_max_ctps = _np.array( [ - seperator_distance, - seperator_distance, - ctps_mid_height_top, - ], - [0.5, seperator_distance, ctps_mid_height_top], - [aux_column_width, 0.5, ctps_mid_height_top], - [seperator_distance, 0.5, ctps_mid_height_top], - [0.5, 0.5, ctps_mid_height_top], - [branch_thickness, branch_thickness, 1.0], - [seperator_distance, aux_column_width, 1.0], - [0.5, aux_column_width, 1.0], - [aux_column_width, seperator_distance, 1.0], - [seperator_distance, seperator_distance, 1.0], - [0.5, seperator_distance, 1.0], - [aux_column_width, 0.5, 1.0], - [seperator_distance, 0.5, 1.0], - [0.5, 0.5, 1.0], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 2, 2], control_points=branch_x_max_y_max_ctps + [r_center, r_center, fill_height_aux], + [half_r_center, r_center, fill_height_aux], + [v_one_half, r_center, fill_height_aux], + [r_center, half_r_center, fill_height_aux], + [half_r_center, half_r_center, fill_height_aux], + [v_one_half, half_r_center, fill_height_aux], + [r_center, v_one_half, fill_height_aux], + [half_r_center, v_one_half, fill_height_aux], + [v_one_half, v_one_half, fill_height_aux], + [ + branch_thickness, + branch_thickness, + ctps_mid_height_top, + ], + [ + sep_distance_aux, + aux_column_width, + ctps_mid_height_top, + ], + [v_one_half, aux_column_width, ctps_mid_height_top], + [ + aux_column_width, + sep_distance_aux, + ctps_mid_height_top, + ], + [ + sep_distance_aux, + sep_distance_aux, + ctps_mid_height_top, + ], + [v_one_half, sep_distance_aux, ctps_mid_height_top], + [aux_column_width, v_one_half, ctps_mid_height_top], + [sep_distance_aux, v_one_half, ctps_mid_height_top], + [v_one_half, v_one_half, ctps_mid_height_top], + [branch_thickness, branch_thickness, v_one], + [sep_distance_aux, aux_column_width, v_one], + [v_one_half, aux_column_width, v_one], + [aux_column_width, sep_distance_aux, v_one], + [sep_distance_aux, sep_distance_aux, v_one], + [v_one_half, sep_distance_aux, v_one], + [aux_column_width, v_one_half, v_one], + [sep_distance_aux, v_one_half, v_one], + [v_one_half, v_one_half, v_one], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 2, 2], + control_points=branch_x_max_y_max_ctps, + ) ) - ) - return (spline_list, None) - - elif closure == "z_max": - branch_thickness = parameters.flatten()[4] - branch_neighbor_x_min_ctps = _np.array( - [ - [-0.5, -aux_column_width, 0.0], - [-seperator_distance, -aux_column_width, 0.0], - [-branch_thickness, -branch_thickness, 0.0], - [-0.5, aux_column_width, 0.0], - [-seperator_distance, aux_column_width, 0.0], - [-branch_thickness, branch_thickness, 0.0], - [-0.5, -aux_column_width, ctps_mid_height_bottom], - [ - -seperator_distance, - -aux_column_width, - ctps_mid_height_bottom, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [-0.5, aux_column_width, ctps_mid_height_bottom], + elif closure == "z_max": + branch_neighbor_x_min_ctps = _np.array( [ - -seperator_distance, - aux_column_width, - ctps_mid_height_bottom, - ], - [ - -branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [-0.5, -r_center, inv_filling_height], - [-half_r_center, -r_center, inv_filling_height], - [-r_center, -r_center, inv_filling_height], - [-0.5, r_center, inv_filling_height], - [-half_r_center, r_center, inv_filling_height], - [-r_center, r_center, inv_filling_height], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 1, 2], - control_points=branch_neighbor_x_min_ctps, + [-v_one_half, -aux_column_width, v_zero], + [-sep_distance_aux, -aux_column_width, v_zero], + [-branch_thickness, -branch_thickness, v_zero], + [-v_one_half, aux_column_width, v_zero], + [-sep_distance_aux, aux_column_width, v_zero], + [-branch_thickness, branch_thickness, v_zero], + [ + -v_one_half, + -aux_column_width, + ctps_mid_height_bottom, + ], + [ + -sep_distance_aux, + -aux_column_width, + ctps_mid_height_bottom, + ], + [ + -branch_thickness, + -branch_thickness, + ctps_mid_height_bottom, + ], + [ + -v_one_half, + aux_column_width, + ctps_mid_height_bottom, + ], + [ + -sep_distance_aux, + aux_column_width, + ctps_mid_height_bottom, + ], + [ + -branch_thickness, + branch_thickness, + ctps_mid_height_bottom, + ], + [-v_one_half, -r_center, inv_filling_height], + [-half_r_center, -r_center, inv_filling_height], + [-r_center, -r_center, inv_filling_height], + [-v_one_half, r_center, inv_filling_height], + [-half_r_center, r_center, inv_filling_height], + [-r_center, r_center, inv_filling_height], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 1, 2], + control_points=branch_neighbor_x_min_ctps, + ) ) - ) - branch_neighbor_x_max_ctps = _np.array( - [ - [branch_thickness, -branch_thickness, 0.0], - [seperator_distance, -aux_column_width, 0.0], - [0.5, -aux_column_width, 0.0], - [branch_thickness, branch_thickness, 0.0], - [seperator_distance, aux_column_width, 0.0], - [0.5, aux_column_width, 0.0], - [ - branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], + branch_neighbor_x_max_ctps = _np.array( [ - seperator_distance, - -aux_column_width, - ctps_mid_height_bottom, - ], - [0.5, -aux_column_width, ctps_mid_height_bottom], - [ - branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - aux_column_width, - ctps_mid_height_bottom, - ], - [0.5, aux_column_width, ctps_mid_height_bottom], - [r_center, -r_center, inv_filling_height], - [half_r_center, -r_center, inv_filling_height], - [0.5, -r_center, inv_filling_height], - [r_center, r_center, inv_filling_height], - [half_r_center, r_center, inv_filling_height], - [0.5, r_center, inv_filling_height], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 1, 2], - control_points=branch_neighbor_x_max_ctps, + [branch_thickness, -branch_thickness, v_zero], + [sep_distance_aux, -aux_column_width, v_zero], + [v_one_half, -aux_column_width, v_zero], + [branch_thickness, branch_thickness, v_zero], + [sep_distance_aux, aux_column_width, v_zero], + [v_one_half, aux_column_width, v_zero], + [ + branch_thickness, + -branch_thickness, + ctps_mid_height_bottom, + ], + [ + sep_distance_aux, + -aux_column_width, + ctps_mid_height_bottom, + ], + [ + v_one_half, + -aux_column_width, + ctps_mid_height_bottom, + ], + [ + branch_thickness, + branch_thickness, + ctps_mid_height_bottom, + ], + [ + sep_distance_aux, + aux_column_width, + ctps_mid_height_bottom, + ], + [v_one_half, aux_column_width, ctps_mid_height_bottom], + [r_center, -r_center, inv_filling_height], + [half_r_center, -r_center, inv_filling_height], + [v_one_half, -r_center, inv_filling_height], + [r_center, r_center, inv_filling_height], + [half_r_center, r_center, inv_filling_height], + [v_one_half, r_center, inv_filling_height], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 1, 2], + control_points=branch_neighbor_x_max_ctps, + ) ) - ) - branch_neighbor_y_min_ctps = _np.array( - [ - [-aux_column_width, -0.5, 0.0], - [aux_column_width, -0.5, 0.0], - [-aux_column_width, -seperator_distance, 0.0], - [aux_column_width, -seperator_distance, 0.0], - [-branch_thickness, -branch_thickness, 0.0], - [branch_thickness, -branch_thickness, 0.0], - [-aux_column_width, -0.5, ctps_mid_height_bottom], - [aux_column_width, -0.5, ctps_mid_height_bottom], - [ - -aux_column_width, - -seperator_distance, - ctps_mid_height_bottom, - ], + branch_neighbor_y_min_ctps = _np.array( [ - aux_column_width, - -seperator_distance, - ctps_mid_height_bottom, - ], - [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [ - branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [-r_center, -0.5, inv_filling_height], - [r_center, -0.5, inv_filling_height], - [-r_center, -half_r_center, inv_filling_height], - [r_center, -half_r_center, inv_filling_height], - [-r_center, -r_center, inv_filling_height], - [r_center, -r_center, inv_filling_height], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[1, 2, 2], - control_points=branch_neighbor_y_min_ctps, + [-aux_column_width, -v_one_half, v_zero], + [aux_column_width, -v_one_half, v_zero], + [-aux_column_width, -sep_distance_aux, v_zero], + [aux_column_width, -sep_distance_aux, v_zero], + [-branch_thickness, -branch_thickness, v_zero], + [branch_thickness, -branch_thickness, v_zero], + [ + -aux_column_width, + -v_one_half, + ctps_mid_height_bottom, + ], + [ + aux_column_width, + -v_one_half, + ctps_mid_height_bottom, + ], + [ + -aux_column_width, + -sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + aux_column_width, + -sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + -branch_thickness, + -branch_thickness, + ctps_mid_height_bottom, + ], + [ + branch_thickness, + -branch_thickness, + ctps_mid_height_bottom, + ], + [-r_center, -v_one_half, inv_filling_height], + [r_center, -v_one_half, inv_filling_height], + [-r_center, -half_r_center, inv_filling_height], + [r_center, -half_r_center, inv_filling_height], + [-r_center, -r_center, inv_filling_height], + [r_center, -r_center, inv_filling_height], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[1, 2, 2], + control_points=branch_neighbor_y_min_ctps, + ) ) - ) - branch_neighbor_y_max_ctps = _np.array( - [ - [-branch_thickness, branch_thickness, 0.0], - [branch_thickness, branch_thickness, 0.0], - [-aux_column_width, seperator_distance, 0.0], - [aux_column_width, seperator_distance, 0.0], - [-aux_column_width, 0.5, 0.0], - [aux_column_width, 0.5, 0.0], + branch_neighbor_y_max_ctps = _np.array( [ - -branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [ - branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [ - -aux_column_width, - seperator_distance, - ctps_mid_height_bottom, - ], - [ - aux_column_width, - seperator_distance, - ctps_mid_height_bottom, - ], - [-aux_column_width, 0.5, ctps_mid_height_bottom], - [aux_column_width, 0.5, ctps_mid_height_bottom], - [-r_center, r_center, inv_filling_height], - [r_center, r_center, inv_filling_height], - [-r_center, half_r_center, inv_filling_height], - [r_center, half_r_center, inv_filling_height], - [-r_center, 0.5, inv_filling_height], - [r_center, 0.5, inv_filling_height], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[1, 2, 2], - control_points=branch_neighbor_y_max_ctps, + [-branch_thickness, branch_thickness, v_zero], + [branch_thickness, branch_thickness, v_zero], + [-aux_column_width, sep_distance_aux, v_zero], + [aux_column_width, sep_distance_aux, v_zero], + [-aux_column_width, v_one_half, v_zero], + [aux_column_width, v_one_half, v_zero], + [ + -branch_thickness, + branch_thickness, + ctps_mid_height_bottom, + ], + [ + branch_thickness, + branch_thickness, + ctps_mid_height_bottom, + ], + [ + -aux_column_width, + sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + aux_column_width, + sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + -aux_column_width, + v_one_half, + ctps_mid_height_bottom, + ], + [aux_column_width, v_one_half, ctps_mid_height_bottom], + [-r_center, r_center, inv_filling_height], + [r_center, r_center, inv_filling_height], + [-r_center, half_r_center, inv_filling_height], + [r_center, half_r_center, inv_filling_height], + [-r_center, v_one_half, inv_filling_height], + [r_center, v_one_half, inv_filling_height], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[1, 2, 2], + control_points=branch_neighbor_y_max_ctps, + ) ) - ) - branch_x_min_y_min_ctps = _np.array( - [ - [-0.5, -0.5, 0.0], - [-seperator_distance, -0.5, 0.0], - [-aux_column_width, -0.5, 0.0], - [-0.5, -seperator_distance, 0.0], - [-seperator_distance, -seperator_distance, 0.0], - [-aux_column_width, -seperator_distance, 0.0], - [-0.5, -aux_column_width, 0.0], - [-seperator_distance, -aux_column_width, 0.0], - [-branch_thickness, -branch_thickness, 0.0], - [-0.5, -0.5, ctps_mid_height_bottom], - [-seperator_distance, -0.5, ctps_mid_height_bottom], - [-aux_column_width, -0.5, ctps_mid_height_bottom], - [-0.5, -seperator_distance, ctps_mid_height_bottom], - [ - -seperator_distance, - -seperator_distance, - ctps_mid_height_bottom, - ], - [ - -aux_column_width, - -seperator_distance, - ctps_mid_height_bottom, - ], - [-0.5, -aux_column_width, ctps_mid_height_bottom], - [ - -seperator_distance, - -aux_column_width, - ctps_mid_height_bottom, - ], + branch_x_min_y_min_ctps = _np.array( [ - -branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [-0.5, -0.5, inv_filling_height], - [-half_r_center, -0.5, inv_filling_height], - [-r_center, -0.5, inv_filling_height], - [-0.5, -half_r_center, inv_filling_height], - [-half_r_center, -half_r_center, inv_filling_height], - [-r_center, -half_r_center, inv_filling_height], - [-0.5, -r_center, inv_filling_height], - [-half_r_center, -r_center, inv_filling_height], - [-r_center, -r_center, inv_filling_height], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 2, 2], control_points=branch_x_min_y_min_ctps + [-v_one_half, -v_one_half, v_zero], + [-sep_distance_aux, -v_one_half, v_zero], + [-aux_column_width, -v_one_half, v_zero], + [-v_one_half, -sep_distance_aux, v_zero], + [-sep_distance_aux, -sep_distance_aux, v_zero], + [-aux_column_width, -sep_distance_aux, v_zero], + [-v_one_half, -aux_column_width, v_zero], + [-sep_distance_aux, -aux_column_width, v_zero], + [-branch_thickness, -branch_thickness, v_zero], + [-v_one_half, -v_one_half, ctps_mid_height_bottom], + [ + -sep_distance_aux, + -v_one_half, + ctps_mid_height_bottom, + ], + [ + -aux_column_width, + -v_one_half, + ctps_mid_height_bottom, + ], + [ + -v_one_half, + -sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + -sep_distance_aux, + -sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + -aux_column_width, + -sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + -v_one_half, + -aux_column_width, + ctps_mid_height_bottom, + ], + [ + -sep_distance_aux, + -aux_column_width, + ctps_mid_height_bottom, + ], + [ + -branch_thickness, + -branch_thickness, + ctps_mid_height_bottom, + ], + [-v_one_half, -v_one_half, inv_filling_height], + [-half_r_center, -v_one_half, inv_filling_height], + [-r_center, -v_one_half, inv_filling_height], + [-v_one_half, -half_r_center, inv_filling_height], + [-half_r_center, -half_r_center, inv_filling_height], + [-r_center, -half_r_center, inv_filling_height], + [-v_one_half, -r_center, inv_filling_height], + [-half_r_center, -r_center, inv_filling_height], + [-r_center, -r_center, inv_filling_height], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 2, 2], + control_points=branch_x_min_y_min_ctps, + ) ) - ) - branch_x_max_y_max_ctps = _np.array( - [ - [branch_thickness, branch_thickness, 0.0], - [seperator_distance, aux_column_width, 0.0], - [0.5, aux_column_width, 0.0], - [aux_column_width, seperator_distance, 0.0], - [seperator_distance, seperator_distance, 0.0], - [0.5, seperator_distance, 0.0], - [aux_column_width, 0.5, 0.0], - [seperator_distance, 0.5, 0.0], - [0.5, 0.5, 0.0], - [ - branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - aux_column_width, - ctps_mid_height_bottom, - ], - [0.5, aux_column_width, ctps_mid_height_bottom], + branch_x_max_y_max_ctps = _np.array( [ - aux_column_width, - seperator_distance, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - seperator_distance, - ctps_mid_height_bottom, - ], - [0.5, seperator_distance, ctps_mid_height_bottom], - [aux_column_width, 0.5, ctps_mid_height_bottom], - [seperator_distance, 0.5, ctps_mid_height_bottom], - [0.5, 0.5, ctps_mid_height_bottom], - [r_center, r_center, inv_filling_height], - [half_r_center, r_center, inv_filling_height], - [0.5, r_center, inv_filling_height], - [r_center, half_r_center, inv_filling_height], - [half_r_center, half_r_center, inv_filling_height], - [0.5, half_r_center, inv_filling_height], - [r_center, 0.5, inv_filling_height], - [half_r_center, 0.5, inv_filling_height], - [0.5, 0.5, inv_filling_height], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 2, 2], control_points=branch_x_max_y_max_ctps + [branch_thickness, branch_thickness, v_zero], + [sep_distance_aux, aux_column_width, v_zero], + [v_one_half, aux_column_width, v_zero], + [aux_column_width, sep_distance_aux, v_zero], + [sep_distance_aux, sep_distance_aux, v_zero], + [v_one_half, sep_distance_aux, v_zero], + [aux_column_width, v_one_half, v_zero], + [sep_distance_aux, v_one_half, v_zero], + [v_one_half, v_one_half, v_zero], + [ + branch_thickness, + branch_thickness, + ctps_mid_height_bottom, + ], + [ + sep_distance_aux, + aux_column_width, + ctps_mid_height_bottom, + ], + [v_one_half, aux_column_width, ctps_mid_height_bottom], + [ + aux_column_width, + sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + sep_distance_aux, + sep_distance_aux, + ctps_mid_height_bottom, + ], + [v_one_half, sep_distance_aux, ctps_mid_height_bottom], + [aux_column_width, v_one_half, ctps_mid_height_bottom], + [sep_distance_aux, v_one_half, ctps_mid_height_bottom], + [v_one_half, v_one_half, ctps_mid_height_bottom], + [r_center, r_center, inv_filling_height], + [half_r_center, r_center, inv_filling_height], + [v_one_half, r_center, inv_filling_height], + [r_center, half_r_center, inv_filling_height], + [half_r_center, half_r_center, inv_filling_height], + [v_one_half, half_r_center, inv_filling_height], + [r_center, v_one_half, inv_filling_height], + [half_r_center, v_one_half, inv_filling_height], + [v_one_half, v_one_half, inv_filling_height], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 2, 2], + control_points=branch_x_max_y_max_ctps, + ) ) - ) - branch_x_max_y_min_ctps = _np.array( - [ - [aux_column_width, -0.5, 0.0], - [seperator_distance, -0.5, 0.0], - [0.5, -0.5, 0.0], - [aux_column_width, -seperator_distance, 0.0], - [seperator_distance, -seperator_distance, 0.0], - [0.5, -seperator_distance, 0.0], - [branch_thickness, -branch_thickness, 0.0], - [seperator_distance, -aux_column_width, 0.0], - [0.5, -aux_column_width, 0.0], - [aux_column_width, -0.5, ctps_mid_height_bottom], - [seperator_distance, -0.5, ctps_mid_height_bottom], - [0.5, -0.5, ctps_mid_height_bottom], - [ - aux_column_width, - -seperator_distance, - ctps_mid_height_bottom, - ], + branch_x_max_y_min_ctps = _np.array( [ - seperator_distance, - -seperator_distance, - ctps_mid_height_bottom, - ], - [0.5, -seperator_distance, ctps_mid_height_bottom], - [ - branch_thickness, - -branch_thickness, - ctps_mid_height_bottom, - ], - [ - seperator_distance, - -aux_column_width, - ctps_mid_height_bottom, - ], - [0.5, -aux_column_width, ctps_mid_height_bottom], - [r_center, -0.5, inv_filling_height], - [half_r_center, -0.5, inv_filling_height], - [0.5, -0.5, inv_filling_height], - [r_center, -half_r_center, inv_filling_height], - [half_r_center, -half_r_center, inv_filling_height], - [0.5, -half_r_center, inv_filling_height], - [r_center, -r_center, inv_filling_height], - [half_r_center, -r_center, inv_filling_height], - [0.5, -r_center, inv_filling_height], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 2, 2], control_points=branch_x_max_y_min_ctps + [aux_column_width, -v_one_half, v_zero], + [sep_distance_aux, -v_one_half, v_zero], + [v_one_half, -v_one_half, v_zero], + [aux_column_width, -sep_distance_aux, v_zero], + [sep_distance_aux, -sep_distance_aux, v_zero], + [v_one_half, -sep_distance_aux, v_zero], + [branch_thickness, -branch_thickness, v_zero], + [sep_distance_aux, -aux_column_width, v_zero], + [v_one_half, -aux_column_width, v_zero], + [ + aux_column_width, + -v_one_half, + ctps_mid_height_bottom, + ], + [ + sep_distance_aux, + -v_one_half, + ctps_mid_height_bottom, + ], + [v_one_half, -v_one_half, ctps_mid_height_bottom], + [ + aux_column_width, + -sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + sep_distance_aux, + -sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + v_one_half, + -sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + branch_thickness, + -branch_thickness, + ctps_mid_height_bottom, + ], + [ + sep_distance_aux, + -aux_column_width, + ctps_mid_height_bottom, + ], + [ + v_one_half, + -aux_column_width, + ctps_mid_height_bottom, + ], + [r_center, -v_one_half, inv_filling_height], + [half_r_center, -v_one_half, inv_filling_height], + [v_one_half, -v_one_half, inv_filling_height], + [r_center, -half_r_center, inv_filling_height], + [half_r_center, -half_r_center, inv_filling_height], + [v_one_half, -half_r_center, inv_filling_height], + [r_center, -r_center, inv_filling_height], + [half_r_center, -r_center, inv_filling_height], + [v_one_half, -r_center, inv_filling_height], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 2, 2], + control_points=branch_x_max_y_min_ctps, + ) ) - ) - branch_x_min_y_max_ctps = _np.array( - [ - [-0.5, aux_column_width, 0.0], - [-seperator_distance, aux_column_width, 0.0], - [-branch_thickness, branch_thickness, 0.0], - [-0.5, seperator_distance, 0.0], - [-seperator_distance, seperator_distance, 0.0], - [-aux_column_width, seperator_distance, 0.0], - [-0.5, 0.5, 0.0], - [-seperator_distance, 0.5, 0.0], - [-aux_column_width, 0.5, 0.0], - [-0.5, aux_column_width, ctps_mid_height_bottom], - [ - -seperator_distance, - aux_column_width, - ctps_mid_height_bottom, - ], + branch_x_min_y_max_ctps = _np.array( [ - -branch_thickness, - branch_thickness, - ctps_mid_height_bottom, - ], - [-0.5, seperator_distance, ctps_mid_height_bottom], - [ - -seperator_distance, - seperator_distance, - ctps_mid_height_bottom, - ], - [ - -aux_column_width, - seperator_distance, - ctps_mid_height_bottom, - ], - [-0.5, 0.5, ctps_mid_height_bottom], - [-seperator_distance, 0.5, ctps_mid_height_bottom], - [-aux_column_width, 0.5, ctps_mid_height_bottom], - [-0.5, r_center, inv_filling_height], - [-half_r_center, r_center, inv_filling_height], - [-r_center, r_center, inv_filling_height], - [-0.5, half_r_center, inv_filling_height], - [-half_r_center, half_r_center, inv_filling_height], - [-r_center, half_r_center, inv_filling_height], - [-0.5, 0.5, inv_filling_height], - [-half_r_center, 0.5, inv_filling_height], - [-r_center, 0.5, inv_filling_height], - ] - ) + _np.array([0.5, 0.5, 0.0]) - - spline_list.append( - _Bezier( - degrees=[2, 2, 2], control_points=branch_x_min_y_max_ctps + [-v_one_half, aux_column_width, v_zero], + [-sep_distance_aux, aux_column_width, v_zero], + [-branch_thickness, branch_thickness, v_zero], + [-v_one_half, sep_distance_aux, v_zero], + [-sep_distance_aux, sep_distance_aux, v_zero], + [-aux_column_width, sep_distance_aux, v_zero], + [-v_one_half, v_one_half, v_zero], + [-sep_distance_aux, v_one_half, v_zero], + [-aux_column_width, v_one_half, v_zero], + [ + -v_one_half, + aux_column_width, + ctps_mid_height_bottom, + ], + [ + -sep_distance_aux, + aux_column_width, + ctps_mid_height_bottom, + ], + [ + -branch_thickness, + branch_thickness, + ctps_mid_height_bottom, + ], + [ + -v_one_half, + sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + -sep_distance_aux, + sep_distance_aux, + ctps_mid_height_bottom, + ], + [ + -aux_column_width, + sep_distance_aux, + ctps_mid_height_bottom, + ], + [-v_one_half, v_one_half, ctps_mid_height_bottom], + [ + -sep_distance_aux, + v_one_half, + ctps_mid_height_bottom, + ], + [ + -aux_column_width, + v_one_half, + ctps_mid_height_bottom, + ], + [-v_one_half, r_center, inv_filling_height], + [-half_r_center, r_center, inv_filling_height], + [-r_center, r_center, inv_filling_height], + [-v_one_half, half_r_center, inv_filling_height], + [-half_r_center, half_r_center, inv_filling_height], + [-r_center, half_r_center, inv_filling_height], + [-v_one_half, v_one_half, inv_filling_height], + [-half_r_center, v_one_half, inv_filling_height], + [-r_center, v_one_half, inv_filling_height], + ] + ) + _np.array([v_one_half, v_one_half, v_zero]) + + spline_list.append( + _Bezier( + degrees=[2, 2, 2], + control_points=branch_x_min_y_max_ctps, + ) ) - ) + else: + raise ValueError("Corner Type not supported") + + if i_derivative == 0: + splines = spline_list.copy() + else: + derivatives.append(spline_list) - return (spline_list, None) - else: - raise ValueError("Corner Type not supported") + return (splines, derivatives) def create_tile( self, parameters=None, parameter_sensitivities=None, - seperator_distance=None, + separator_distance=0.3, center_expansion=1.0, closure=None, **kwargs, # noqa ARG002 @@ -905,9 +1039,9 @@ def create_tile( parameters : np.array only first entry is used, defines the internal radii of the branches - seperator_distance : float + separator_distance : float Control point distance to separation layer of biquadratic degrees, - determines the minimum branch thickness (defaults to 0.4) + determines the minimum branch thickness (defaults to 0.3) center_expansion : float thickness of center is expanded by a factor (default to 1.0), which determines the maximum branch thickness @@ -920,46 +1054,25 @@ def create_tile( microtile_list : list(splines) """ - if not isinstance(center_expansion, float): - raise ValueError("Invalid Type") - - if not ((center_expansion > 0.5) and (center_expansion < 1.5)): - raise ValueError("Center Expansion must be in (.5, 1.5)") - - # Set default values - if seperator_distance is None: - seperator_distance = 0.3 - - if center_expansion is None: - center_expansion = 1.0 + self._check_custom_parameter( + center_expansion, "center expansion", self._CENTER_EXPANSION_BOUNDS + ) # Check if all radii are in allowed range max_radius = min(0.5, (0.5 / center_expansion)) - max_radius = min(max_radius, seperator_distance) - min_radius = max(0.5 - seperator_distance, 0) - - # set to default if nothing is given - if parameters is None: - self._logd("Setting branch thickness to default 0.2") - parameters = ( - _np.ones( - (len(self._evaluation_points), self._n_info_per_eval_point) - ) - * 0.2 - ) - - if parameter_sensitivities is not None: - raise NotImplementedError( - "Derivatives are not implemented for this tile yet" - ) + max_radius = min(max_radius, separator_distance) + min_radius = max(0.5 - separator_distance, 0) - self.check_params(parameters) + parameters, n_derivatives, derivatives = self._process_input( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + ) if _np.any(parameters < min_radius) or _np.any( parameters > max_radius ): raise ValueError( - f"Radii must be in (0,{max_radius}) for " + f"Radii must be in ({min_radius},{max_radius}) for " f"center_expansion {center_expansion}" ) @@ -967,649 +1080,715 @@ def create_tile( return self._closing_tile( parameters=parameters, parameter_sensitivities=parameter_sensitivities, - seperator_distance=seperator_distance, + separator_distance=separator_distance, closure=closure, **kwargs, ) - [ - x_min_r, - x_max_r, - y_min_r, - y_max_r, - z_min_r, - z_max_r, - ] = parameters.flatten() - - # center radius - center_r = _np.sum(parameters) / 6.0 * center_expansion - - # Auxiliary values for smooothing (mid-branch thickness) - [ - aux_x_min, - aux_x_max, - aux_y_min, - aux_y_max, - aux_z_min, - aux_z_max, - ] = _np.minimum(parameters.ravel(), center_r) - # Branch midlength - hd_center = 0.5 * (0.5 + center_r) - aux_column_width = 0.5 - 2 * (0.5 - seperator_distance) - - # Init return type - spline_list = [] - - # Start with branch interconnections - x_min_y_min = _np.array( - [ - [-0.5, -0.5, -aux_column_width], - [-seperator_distance, -0.5, -aux_column_width], - [-y_min_r, -0.5, -y_min_r], - [-0.5, -seperator_distance, -aux_column_width], - [-hd_center, -hd_center, -aux_column_width], - [-aux_y_min, -hd_center, -aux_y_min], - [-0.5, -x_min_r, -x_min_r], - [-hd_center, -aux_x_min, -aux_x_min], - [-center_r, -center_r, -center_r], - [-0.5, -0.5, aux_column_width], - [-seperator_distance, -0.5, aux_column_width], - [-y_min_r, -0.5, y_min_r], - [-0.5, -seperator_distance, aux_column_width], - [-hd_center, -hd_center, aux_column_width], - [-aux_y_min, -hd_center, aux_y_min], - [-0.5, -x_min_r, x_min_r], - [-hd_center, -aux_x_min, aux_x_min], - [-center_r, -center_r, center_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 1], control_points=x_min_y_min) - ) + splines = [] + for i_derivative in range(n_derivatives + 1): + if i_derivative == 0: + [ + x_min_r, + x_max_r, + y_min_r, + y_max_r, + z_min_r, + z_max_r, + ] = parameters.flatten() + + # center radius + center_r = _np.sum(parameters) / 6.0 * center_expansion + + # Auxiliary values for smooothing (mid-branch thickness) + [ + aux_x_min, + aux_x_max, + aux_y_min, + aux_y_max, + aux_z_min, + aux_z_max, + ] = _np.minimum(parameters.ravel(), center_r) + # Branch midlength + hd_center = 0.5 * (0.5 + center_r) + aux_column_width = 0.5 - 2 * (0.5 - separator_distance) + + v_one_half = 0.5 + center_point = _np.array([0.5, 0.5, 0.5]) + + sep_distance = separator_distance + else: + sensitivities_i = parameter_sensitivities[ + :, 0, i_derivative - 1 + ] + [ + x_min_r, + x_max_r, + y_min_r, + y_max_r, + z_min_r, + z_max_r, + ] = sensitivities_i + + center_r = _np.mean(sensitivities_i) * center_expansion + [ + aux_x_min, + aux_x_max, + aux_y_min, + aux_y_max, + aux_z_min, + aux_z_max, + ] = _np.where( + parameters.ravel() + < _np.mean(parameters.ravel()) * center_expansion, + sensitivities_i, + center_r, + ) + # Branch midlength + hd_center = center_r / 2.0 + aux_column_width = 0.0 + v_one_half = 0.0 + center_point = _np.zeros(3) - x_max_y_min = _np.array( - [ - [y_min_r, -0.5, -y_min_r], - [seperator_distance, -0.5, -aux_column_width], - [0.5, -0.5, -aux_column_width], - [aux_y_min, -hd_center, -aux_y_min], - [hd_center, -hd_center, -aux_column_width], - [0.5, -seperator_distance, -aux_column_width], - [center_r, -center_r, -center_r], - [hd_center, -aux_x_max, -aux_x_max], - [0.5, -x_max_r, -x_max_r], - [y_min_r, -0.5, y_min_r], - [seperator_distance, -0.5, aux_column_width], - [0.5, -0.5, aux_column_width], - [aux_y_min, -hd_center, aux_y_min], - [hd_center, -hd_center, aux_column_width], - [0.5, -seperator_distance, aux_column_width], - [center_r, -center_r, center_r], - [hd_center, -aux_x_max, aux_x_max], - [0.5, -x_max_r, x_max_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 1], control_points=x_max_y_min) - ) + sep_distance = 0.0 - x_min_y_max = _np.array( - [ - [-0.5, x_min_r, -x_min_r], - [-hd_center, aux_x_min, -aux_x_min], - [-center_r, center_r, -center_r], - [-0.5, seperator_distance, -aux_column_width], - [-hd_center, hd_center, -aux_column_width], - [-aux_y_max, hd_center, -aux_y_max], - [-0.5, 0.5, -aux_column_width], - [-seperator_distance, 0.5, -aux_column_width], - [-y_max_r, 0.5, -y_max_r], - [-0.5, x_min_r, x_min_r], - [-hd_center, aux_x_min, aux_x_min], - [-center_r, center_r, center_r], - [-0.5, seperator_distance, aux_column_width], - [-hd_center, hd_center, aux_column_width], - [-aux_y_max, hd_center, aux_y_max], - [-0.5, 0.5, aux_column_width], - [-seperator_distance, 0.5, aux_column_width], - [-y_max_r, 0.5, y_max_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 1], control_points=x_min_y_max) - ) + # Init return type + spline_list = [] - x_max_y_max = _np.array( - [ - [center_r, center_r, -center_r], - [hd_center, aux_x_max, -aux_x_max], - [0.5, x_max_r, -x_max_r], - [aux_y_max, hd_center, -aux_y_max], - [hd_center, hd_center, -aux_column_width], - [0.5, seperator_distance, -aux_column_width], - [y_max_r, 0.5, -y_max_r], - [seperator_distance, 0.5, -aux_column_width], - [0.5, 0.5, -aux_column_width], - [center_r, center_r, center_r], - [hd_center, aux_x_max, aux_x_max], - [0.5, x_max_r, x_max_r], - [aux_y_max, hd_center, aux_y_max], - [hd_center, hd_center, aux_column_width], - [0.5, seperator_distance, aux_column_width], - [y_max_r, 0.5, y_max_r], - [seperator_distance, 0.5, aux_column_width], - [0.5, 0.5, aux_column_width], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 1], control_points=x_max_y_max) - ) + # Start with branch interconnections + x_min_y_min = ( + _np.array( + [ + [-v_one_half, -v_one_half, -aux_column_width], + [-sep_distance, -v_one_half, -aux_column_width], + [-y_min_r, -v_one_half, -y_min_r], + [-v_one_half, -sep_distance, -aux_column_width], + [-hd_center, -hd_center, -aux_column_width], + [-aux_y_min, -hd_center, -aux_y_min], + [-v_one_half, -x_min_r, -x_min_r], + [-hd_center, -aux_x_min, -aux_x_min], + [-center_r, -center_r, -center_r], + [-v_one_half, -v_one_half, aux_column_width], + [-sep_distance, -v_one_half, aux_column_width], + [-y_min_r, -v_one_half, y_min_r], + [-v_one_half, -sep_distance, aux_column_width], + [-hd_center, -hd_center, aux_column_width], + [-aux_y_min, -hd_center, aux_y_min], + [-v_one_half, -x_min_r, x_min_r], + [-hd_center, -aux_x_min, aux_x_min], + [-center_r, -center_r, center_r], + ] + ) + + center_point + ) - x_min_z_min = _np.array( - [ - [-0.5, -aux_column_width, -0.5], - [-seperator_distance, -aux_column_width, -0.5], - [-z_min_r, -z_min_r, -0.5], - [-0.5, aux_column_width, -0.5], - [-seperator_distance, aux_column_width, -0.5], - [-z_min_r, z_min_r, -0.5], - [-0.5, -aux_column_width, -seperator_distance], - [-hd_center, -aux_column_width, -hd_center], - [-aux_z_min, -aux_z_min, -hd_center], - [-0.5, aux_column_width, -seperator_distance], - [-hd_center, aux_column_width, -hd_center], - [-aux_z_min, aux_z_min, -hd_center], - [-0.5, -x_min_r, -x_min_r], - [-hd_center, -aux_x_min, -aux_x_min], - [-center_r, -center_r, -center_r], - [-0.5, x_min_r, -x_min_r], - [-hd_center, aux_x_min, -aux_x_min], - [-center_r, center_r, -center_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 1, 2], control_points=x_min_z_min) - ) + x_max_y_min = ( + _np.array( + [ + [y_min_r, -v_one_half, -y_min_r], + [sep_distance, -v_one_half, -aux_column_width], + [v_one_half, -v_one_half, -aux_column_width], + [aux_y_min, -hd_center, -aux_y_min], + [hd_center, -hd_center, -aux_column_width], + [v_one_half, -sep_distance, -aux_column_width], + [center_r, -center_r, -center_r], + [hd_center, -aux_x_max, -aux_x_max], + [v_one_half, -x_max_r, -x_max_r], + [y_min_r, -v_one_half, y_min_r], + [sep_distance, -v_one_half, aux_column_width], + [v_one_half, -v_one_half, aux_column_width], + [aux_y_min, -hd_center, aux_y_min], + [hd_center, -hd_center, aux_column_width], + [v_one_half, -sep_distance, aux_column_width], + [center_r, -center_r, center_r], + [hd_center, -aux_x_max, aux_x_max], + [v_one_half, -x_max_r, x_max_r], + ] + ) + + center_point + ) - x_max_z_min = _np.array( - [ - [z_min_r, -z_min_r, -0.5], - [seperator_distance, -aux_column_width, -0.5], - [0.5, -aux_column_width, -0.5], - [z_min_r, z_min_r, -0.5], - [seperator_distance, aux_column_width, -0.5], - [0.5, aux_column_width, -0.5], - [aux_z_min, -aux_z_min, -hd_center], - [hd_center, -aux_column_width, -hd_center], - [0.5, -aux_column_width, -seperator_distance], - [aux_z_min, aux_z_min, -hd_center], - [hd_center, aux_column_width, -hd_center], - [0.5, aux_column_width, -seperator_distance], - [center_r, -center_r, -center_r], - [hd_center, -aux_x_max, -aux_x_max], - [0.5, -x_max_r, -x_max_r], - [center_r, center_r, -center_r], - [hd_center, aux_x_max, -aux_x_max], - [0.5, x_max_r, -x_max_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 1, 2], control_points=x_max_z_min) - ) + x_min_y_max = ( + _np.array( + [ + [-v_one_half, x_min_r, -x_min_r], + [-hd_center, aux_x_min, -aux_x_min], + [-center_r, center_r, -center_r], + [-v_one_half, sep_distance, -aux_column_width], + [-hd_center, hd_center, -aux_column_width], + [-aux_y_max, hd_center, -aux_y_max], + [-v_one_half, v_one_half, -aux_column_width], + [-sep_distance, v_one_half, -aux_column_width], + [-y_max_r, v_one_half, -y_max_r], + [-v_one_half, x_min_r, x_min_r], + [-hd_center, aux_x_min, aux_x_min], + [-center_r, center_r, center_r], + [-v_one_half, sep_distance, aux_column_width], + [-hd_center, hd_center, aux_column_width], + [-aux_y_max, hd_center, aux_y_max], + [-v_one_half, v_one_half, aux_column_width], + [-sep_distance, v_one_half, aux_column_width], + [-y_max_r, v_one_half, y_max_r], + ] + ) + + center_point + ) - x_min_z_max = _np.array( - [ - [-0.5, -x_min_r, x_min_r], - [-hd_center, -aux_x_min, aux_x_min], - [-center_r, -center_r, center_r], - [-0.5, x_min_r, x_min_r], - [-hd_center, aux_x_min, aux_x_min], - [-center_r, center_r, center_r], - [-0.5, -aux_column_width, seperator_distance], - [-hd_center, -aux_column_width, hd_center], - [-aux_z_max, -aux_z_max, hd_center], - [-0.5, aux_column_width, seperator_distance], - [-hd_center, aux_column_width, hd_center], - [-aux_z_max, aux_z_max, hd_center], - [-0.5, -aux_column_width, 0.5], - [-seperator_distance, -aux_column_width, 0.5], - [-z_max_r, -z_max_r, 0.5], - [-0.5, aux_column_width, 0.5], - [-seperator_distance, aux_column_width, 0.5], - [-z_max_r, z_max_r, 0.5], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 1, 2], control_points=x_min_z_max) - ) + x_max_y_max = ( + _np.array( + [ + [center_r, center_r, -center_r], + [hd_center, aux_x_max, -aux_x_max], + [v_one_half, x_max_r, -x_max_r], + [aux_y_max, hd_center, -aux_y_max], + [hd_center, hd_center, -aux_column_width], + [v_one_half, sep_distance, -aux_column_width], + [y_max_r, v_one_half, -y_max_r], + [sep_distance, v_one_half, -aux_column_width], + [v_one_half, v_one_half, -aux_column_width], + [center_r, center_r, center_r], + [hd_center, aux_x_max, aux_x_max], + [v_one_half, x_max_r, x_max_r], + [aux_y_max, hd_center, aux_y_max], + [hd_center, hd_center, aux_column_width], + [v_one_half, sep_distance, aux_column_width], + [y_max_r, v_one_half, y_max_r], + [sep_distance, v_one_half, aux_column_width], + [v_one_half, v_one_half, aux_column_width], + ] + ) + + center_point + ) - x_max_z_max = _np.array( - [ - [center_r, -center_r, center_r], - [hd_center, -aux_x_max, aux_x_max], - [0.5, -x_max_r, x_max_r], - [center_r, center_r, center_r], - [hd_center, aux_x_max, aux_x_max], - [0.5, x_max_r, x_max_r], - [aux_z_max, -aux_z_max, hd_center], - [hd_center, -aux_column_width, hd_center], - [0.5, -aux_column_width, seperator_distance], - [aux_z_max, aux_z_max, hd_center], - [hd_center, aux_column_width, hd_center], - [0.5, aux_column_width, seperator_distance], - [z_max_r, -z_max_r, 0.5], - [seperator_distance, -aux_column_width, 0.5], - [0.5, -aux_column_width, 0.5], - [z_max_r, z_max_r, 0.5], - [seperator_distance, aux_column_width, 0.5], - [0.5, aux_column_width, 0.5], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 1, 2], control_points=x_max_z_max) - ) + x_min_z_min = ( + _np.array( + [ + [-v_one_half, -aux_column_width, -v_one_half], + [-sep_distance, -aux_column_width, -v_one_half], + [-z_min_r, -z_min_r, -v_one_half], + [-v_one_half, aux_column_width, -v_one_half], + [-sep_distance, aux_column_width, -v_one_half], + [-z_min_r, z_min_r, -v_one_half], + [-v_one_half, -aux_column_width, -sep_distance], + [-hd_center, -aux_column_width, -hd_center], + [-aux_z_min, -aux_z_min, -hd_center], + [-v_one_half, aux_column_width, -sep_distance], + [-hd_center, aux_column_width, -hd_center], + [-aux_z_min, aux_z_min, -hd_center], + [-v_one_half, -x_min_r, -x_min_r], + [-hd_center, -aux_x_min, -aux_x_min], + [-center_r, -center_r, -center_r], + [-v_one_half, x_min_r, -x_min_r], + [-hd_center, aux_x_min, -aux_x_min], + [-center_r, center_r, -center_r], + ] + ) + + center_point + ) - y_min_z_min = _np.array( - [ - [-aux_column_width, -0.5, -0.5], - [aux_column_width, -0.5, -0.5], - [-aux_column_width, -seperator_distance, -0.5], - [aux_column_width, -seperator_distance, -0.5], - [-z_min_r, -z_min_r, -0.5], - [z_min_r, -z_min_r, -0.5], - [-aux_column_width, -0.5, -seperator_distance], - [aux_column_width, -0.5, -seperator_distance], - [-aux_column_width, -hd_center, -hd_center], - [aux_column_width, -hd_center, -hd_center], - [-aux_z_min, -aux_z_min, -hd_center], - [aux_z_min, -aux_z_min, -hd_center], - [-y_min_r, -0.5, -y_min_r], - [y_min_r, -0.5, -y_min_r], - [-aux_y_min, -hd_center, -aux_y_min], - [aux_y_min, -hd_center, -aux_y_min], - [-center_r, -center_r, -center_r], - [center_r, -center_r, -center_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[1, 2, 2], control_points=y_min_z_min) - ) + x_max_z_min = ( + _np.array( + [ + [z_min_r, -z_min_r, -v_one_half], + [sep_distance, -aux_column_width, -v_one_half], + [v_one_half, -aux_column_width, -v_one_half], + [z_min_r, z_min_r, -v_one_half], + [sep_distance, aux_column_width, -v_one_half], + [v_one_half, aux_column_width, -v_one_half], + [aux_z_min, -aux_z_min, -hd_center], + [hd_center, -aux_column_width, -hd_center], + [v_one_half, -aux_column_width, -sep_distance], + [aux_z_min, aux_z_min, -hd_center], + [hd_center, aux_column_width, -hd_center], + [v_one_half, aux_column_width, -sep_distance], + [center_r, -center_r, -center_r], + [hd_center, -aux_x_max, -aux_x_max], + [v_one_half, -x_max_r, -x_max_r], + [center_r, center_r, -center_r], + [hd_center, aux_x_max, -aux_x_max], + [v_one_half, x_max_r, -x_max_r], + ] + ) + + center_point + ) - y_max_z_min = _np.array( - [ - [-z_min_r, z_min_r, -0.5], - [z_min_r, z_min_r, -0.5], - [-aux_column_width, seperator_distance, -0.5], - [aux_column_width, seperator_distance, -0.5], - [-aux_column_width, 0.5, -0.5], - [aux_column_width, 0.5, -0.5], - [-aux_z_min, aux_z_min, -hd_center], - [aux_z_min, aux_z_min, -hd_center], - [-aux_column_width, hd_center, -hd_center], - [aux_column_width, hd_center, -hd_center], - [-aux_column_width, 0.5, -seperator_distance], - [aux_column_width, 0.5, -seperator_distance], - [-center_r, center_r, -center_r], - [center_r, center_r, -center_r], - [-aux_y_max, hd_center, -aux_y_max], - [aux_y_max, hd_center, -aux_y_max], - [-y_max_r, 0.5, -y_max_r], - [y_max_r, 0.5, -y_max_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[1, 2, 2], control_points=y_max_z_min) - ) + x_min_z_max = ( + _np.array( + [ + [-v_one_half, -x_min_r, x_min_r], + [-hd_center, -aux_x_min, aux_x_min], + [-center_r, -center_r, center_r], + [-v_one_half, x_min_r, x_min_r], + [-hd_center, aux_x_min, aux_x_min], + [-center_r, center_r, center_r], + [-v_one_half, -aux_column_width, sep_distance], + [-hd_center, -aux_column_width, hd_center], + [-aux_z_max, -aux_z_max, hd_center], + [-v_one_half, aux_column_width, sep_distance], + [-hd_center, aux_column_width, hd_center], + [-aux_z_max, aux_z_max, hd_center], + [-v_one_half, -aux_column_width, v_one_half], + [-sep_distance, -aux_column_width, v_one_half], + [-z_max_r, -z_max_r, v_one_half], + [-v_one_half, aux_column_width, v_one_half], + [-sep_distance, aux_column_width, v_one_half], + [-z_max_r, z_max_r, v_one_half], + ] + ) + + center_point + ) - y_min_z_max = _np.array( - [ - [-y_min_r, -0.5, y_min_r], - [y_min_r, -0.5, y_min_r], - [-aux_y_min, -hd_center, aux_y_min], - [aux_y_min, -hd_center, aux_y_min], - [-center_r, -center_r, center_r], - [center_r, -center_r, center_r], - [-aux_column_width, -0.5, seperator_distance], - [aux_column_width, -0.5, seperator_distance], - [-aux_column_width, -hd_center, hd_center], - [aux_column_width, -hd_center, hd_center], - [-aux_z_max, -aux_z_max, hd_center], - [aux_z_max, -aux_z_max, hd_center], - [-aux_column_width, -0.5, 0.5], - [aux_column_width, -0.5, 0.5], - [-aux_column_width, -seperator_distance, 0.5], - [aux_column_width, -seperator_distance, 0.5], - [-z_max_r, -z_max_r, 0.5], - [z_max_r, -z_max_r, 0.5], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[1, 2, 2], control_points=y_min_z_max) - ) + x_max_z_max = ( + _np.array( + [ + [center_r, -center_r, center_r], + [hd_center, -aux_x_max, aux_x_max], + [v_one_half, -x_max_r, x_max_r], + [center_r, center_r, center_r], + [hd_center, aux_x_max, aux_x_max], + [v_one_half, x_max_r, x_max_r], + [aux_z_max, -aux_z_max, hd_center], + [hd_center, -aux_column_width, hd_center], + [v_one_half, -aux_column_width, sep_distance], + [aux_z_max, aux_z_max, hd_center], + [hd_center, aux_column_width, hd_center], + [v_one_half, aux_column_width, sep_distance], + [z_max_r, -z_max_r, v_one_half], + [sep_distance, -aux_column_width, v_one_half], + [v_one_half, -aux_column_width, v_one_half], + [z_max_r, z_max_r, v_one_half], + [sep_distance, aux_column_width, v_one_half], + [v_one_half, aux_column_width, v_one_half], + ] + ) + + center_point + ) - y_max_z_max = _np.array( - [ - [-center_r, center_r, center_r], - [center_r, center_r, center_r], - [-aux_y_max, hd_center, aux_y_max], - [aux_y_max, hd_center, aux_y_max], - [-y_max_r, 0.5, y_max_r], - [y_max_r, 0.5, y_max_r], - [-aux_z_max, aux_z_max, hd_center], - [aux_z_max, aux_z_max, hd_center], - [-aux_column_width, hd_center, hd_center], - [aux_column_width, hd_center, hd_center], - [-aux_column_width, 0.5, seperator_distance], - [aux_column_width, 0.5, seperator_distance], - [-z_max_r, z_max_r, 0.5], - [z_max_r, z_max_r, 0.5], - [-aux_column_width, seperator_distance, 0.5], - [aux_column_width, seperator_distance, 0.5], - [-aux_column_width, 0.5, 0.5], - [aux_column_width, 0.5, 0.5], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[1, 2, 2], control_points=y_max_z_max) - ) + y_min_z_min = ( + _np.array( + [ + [-aux_column_width, -v_one_half, -v_one_half], + [aux_column_width, -v_one_half, -v_one_half], + [-aux_column_width, -sep_distance, -v_one_half], + [aux_column_width, -sep_distance, -v_one_half], + [-z_min_r, -z_min_r, -v_one_half], + [z_min_r, -z_min_r, -v_one_half], + [-aux_column_width, -v_one_half, -sep_distance], + [aux_column_width, -v_one_half, -sep_distance], + [-aux_column_width, -hd_center, -hd_center], + [aux_column_width, -hd_center, -hd_center], + [-aux_z_min, -aux_z_min, -hd_center], + [aux_z_min, -aux_z_min, -hd_center], + [-y_min_r, -v_one_half, -y_min_r], + [y_min_r, -v_one_half, -y_min_r], + [-aux_y_min, -hd_center, -aux_y_min], + [aux_y_min, -hd_center, -aux_y_min], + [-center_r, -center_r, -center_r], + [center_r, -center_r, -center_r], + ] + ) + + center_point + ) - x_min_y_min_z_min = _np.array( - [ - [-0.5, -0.5, -0.5], - [-seperator_distance, -0.5, -0.5], - [-aux_column_width, -0.5, -0.5], - [-0.5, -seperator_distance, -0.5], - [-seperator_distance, -seperator_distance, -0.5], - [-aux_column_width, -seperator_distance, -0.5], - [-0.5, -aux_column_width, -0.5], - [-seperator_distance, -aux_column_width, -0.5], - [-z_min_r, -z_min_r, -0.5], - [-0.5, -0.5, -seperator_distance], - [-seperator_distance, -0.5, -seperator_distance], - [-aux_column_width, -0.5, -seperator_distance], - [-0.5, -seperator_distance, -seperator_distance], - [-hd_center, -hd_center, -hd_center], - [-aux_column_width, -hd_center, -hd_center], - [-0.5, -aux_column_width, -seperator_distance], - [-hd_center, -aux_column_width, -hd_center], - [-aux_z_min, -aux_z_min, -hd_center], - [-0.5, -0.5, -aux_column_width], - [-seperator_distance, -0.5, -aux_column_width], - [-y_min_r, -0.5, -y_min_r], - [-0.5, -seperator_distance, -aux_column_width], - [-hd_center, -hd_center, -aux_column_width], - [-aux_y_min, -hd_center, -aux_y_min], - [-0.5, -x_min_r, -x_min_r], - [-hd_center, -aux_x_min, -aux_x_min], - [-center_r, -center_r, -center_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 2], control_points=x_min_y_min_z_min) - ) + y_max_z_min = ( + _np.array( + [ + [-z_min_r, z_min_r, -v_one_half], + [z_min_r, z_min_r, -v_one_half], + [-aux_column_width, sep_distance, -v_one_half], + [aux_column_width, sep_distance, -v_one_half], + [-aux_column_width, v_one_half, -v_one_half], + [aux_column_width, v_one_half, -v_one_half], + [-aux_z_min, aux_z_min, -hd_center], + [aux_z_min, aux_z_min, -hd_center], + [-aux_column_width, hd_center, -hd_center], + [aux_column_width, hd_center, -hd_center], + [-aux_column_width, v_one_half, -sep_distance], + [aux_column_width, v_one_half, -sep_distance], + [-center_r, center_r, -center_r], + [center_r, center_r, -center_r], + [-aux_y_max, hd_center, -aux_y_max], + [aux_y_max, hd_center, -aux_y_max], + [-y_max_r, v_one_half, -y_max_r], + [y_max_r, v_one_half, -y_max_r], + ] + ) + + center_point + ) - x_max_y_min_z_min = _np.array( - [ - [aux_column_width, -0.5, -0.5], - [seperator_distance, -0.5, -0.5], - [0.5, -0.5, -0.5], - [aux_column_width, -seperator_distance, -0.5], - [seperator_distance, -seperator_distance, -0.5], - [0.5, -seperator_distance, -0.5], - [z_min_r, -z_min_r, -0.5], - [seperator_distance, -aux_column_width, -0.5], - [0.5, -aux_column_width, -0.5], - [aux_column_width, -0.5, -seperator_distance], - [seperator_distance, -0.5, -seperator_distance], - [0.5, -0.5, -seperator_distance], - [aux_column_width, -hd_center, -hd_center], - [hd_center, -hd_center, -hd_center], - [0.5, -seperator_distance, -seperator_distance], - [aux_z_min, -aux_z_min, -hd_center], - [hd_center, -aux_column_width, -hd_center], - [0.5, -aux_column_width, -seperator_distance], - [y_min_r, -0.5, -y_min_r], - [seperator_distance, -0.5, -aux_column_width], - [0.5, -0.5, -aux_column_width], - [aux_y_min, -hd_center, -aux_y_min], - [hd_center, -hd_center, -aux_column_width], - [0.5, -seperator_distance, -aux_column_width], - [center_r, -center_r, -center_r], - [hd_center, -aux_x_max, -aux_x_max], - [0.5, -x_max_r, -x_max_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 2], control_points=x_max_y_min_z_min) - ) + y_min_z_max = ( + _np.array( + [ + [-y_min_r, -v_one_half, y_min_r], + [y_min_r, -v_one_half, y_min_r], + [-aux_y_min, -hd_center, aux_y_min], + [aux_y_min, -hd_center, aux_y_min], + [-center_r, -center_r, center_r], + [center_r, -center_r, center_r], + [-aux_column_width, -v_one_half, sep_distance], + [aux_column_width, -v_one_half, sep_distance], + [-aux_column_width, -hd_center, hd_center], + [aux_column_width, -hd_center, hd_center], + [-aux_z_max, -aux_z_max, hd_center], + [aux_z_max, -aux_z_max, hd_center], + [-aux_column_width, -v_one_half, v_one_half], + [aux_column_width, -v_one_half, v_one_half], + [-aux_column_width, -sep_distance, v_one_half], + [aux_column_width, -sep_distance, v_one_half], + [-z_max_r, -z_max_r, v_one_half], + [z_max_r, -z_max_r, v_one_half], + ] + ) + + center_point + ) - x_min_y_max_z_min = _np.array( - [ - [-0.5, aux_column_width, -0.5], - [-seperator_distance, aux_column_width, -0.5], - [-z_min_r, z_min_r, -0.5], - [-0.5, seperator_distance, -0.5], - [-seperator_distance, seperator_distance, -0.5], - [-aux_column_width, seperator_distance, -0.5], - [-0.5, 0.5, -0.5], - [-seperator_distance, 0.5, -0.5], - [-aux_column_width, 0.5, -0.5], - [-0.5, aux_column_width, -seperator_distance], - [-hd_center, aux_column_width, -hd_center], - [-aux_z_min, aux_z_min, -hd_center], - [-0.5, seperator_distance, -seperator_distance], - [-hd_center, hd_center, -hd_center], - [-aux_column_width, hd_center, -hd_center], - [-0.5, 0.5, -seperator_distance], - [-seperator_distance, 0.5, -seperator_distance], - [-aux_column_width, 0.5, -seperator_distance], - [-0.5, x_min_r, -x_min_r], - [-hd_center, aux_x_min, -aux_x_min], - [-center_r, center_r, -center_r], - [-0.5, seperator_distance, -aux_column_width], - [-hd_center, hd_center, -aux_column_width], - [-aux_y_max, hd_center, -aux_y_max], - [-0.5, 0.5, -aux_column_width], - [-seperator_distance, 0.5, -aux_column_width], - [-y_max_r, 0.5, -y_max_r], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 2], control_points=x_min_y_max_z_min) - ) + y_max_z_max = ( + _np.array( + [ + [-center_r, center_r, center_r], + [center_r, center_r, center_r], + [-aux_y_max, hd_center, aux_y_max], + [aux_y_max, hd_center, aux_y_max], + [-y_max_r, v_one_half, y_max_r], + [y_max_r, v_one_half, y_max_r], + [-aux_z_max, aux_z_max, hd_center], + [aux_z_max, aux_z_max, hd_center], + [-aux_column_width, hd_center, hd_center], + [aux_column_width, hd_center, hd_center], + [-aux_column_width, v_one_half, sep_distance], + [aux_column_width, v_one_half, sep_distance], + [-z_max_r, z_max_r, v_one_half], + [z_max_r, z_max_r, v_one_half], + [-aux_column_width, sep_distance, v_one_half], + [aux_column_width, sep_distance, v_one_half], + [-aux_column_width, v_one_half, v_one_half], + [aux_column_width, v_one_half, v_one_half], + ] + ) + + center_point + ) - x_max_y_max_z_min = _np.array( - [ - [z_min_r, z_min_r, -0.5], - [seperator_distance, aux_column_width, -0.5], - [0.5, aux_column_width, -0.5], - [aux_column_width, seperator_distance, -0.5], - [seperator_distance, seperator_distance, -0.5], - [0.5, seperator_distance, -0.5], - [aux_column_width, 0.5, -0.5], - [seperator_distance, 0.5, -0.5], - [0.5, 0.5, -0.5], - [aux_z_min, aux_z_min, -hd_center], - [hd_center, aux_column_width, -hd_center], - [0.5, aux_column_width, -seperator_distance], - [aux_column_width, hd_center, -hd_center], - [hd_center, hd_center, -hd_center], - [0.5, seperator_distance, -seperator_distance], - [aux_column_width, 0.5, -seperator_distance], - [seperator_distance, 0.5, -seperator_distance], - [0.5, 0.5, -seperator_distance], - [center_r, center_r, -center_r], - [hd_center, aux_x_max, -aux_x_max], - [0.5, x_max_r, -x_max_r], - [aux_y_max, hd_center, -aux_y_max], - [hd_center, hd_center, -aux_column_width], - [0.5, seperator_distance, -aux_column_width], - [y_max_r, 0.5, -y_max_r], - [seperator_distance, 0.5, -aux_column_width], - [0.5, 0.5, -aux_column_width], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 2], control_points=x_max_y_max_z_min) - ) + x_min_y_min_z_min = ( + _np.array( + [ + [-v_one_half, -v_one_half, -v_one_half], + [-sep_distance, -v_one_half, -v_one_half], + [-aux_column_width, -v_one_half, -v_one_half], + [-v_one_half, -sep_distance, -v_one_half], + [ + -sep_distance, + -sep_distance, + -v_one_half, + ], + [-aux_column_width, -sep_distance, -v_one_half], + [-v_one_half, -aux_column_width, -v_one_half], + [-sep_distance, -aux_column_width, -v_one_half], + [-z_min_r, -z_min_r, -v_one_half], + [-v_one_half, -v_one_half, -sep_distance], + [ + -sep_distance, + -v_one_half, + -sep_distance, + ], + [-aux_column_width, -v_one_half, -sep_distance], + [ + -v_one_half, + -sep_distance, + -sep_distance, + ], + [-hd_center, -hd_center, -hd_center], + [-aux_column_width, -hd_center, -hd_center], + [-v_one_half, -aux_column_width, -sep_distance], + [-hd_center, -aux_column_width, -hd_center], + [-aux_z_min, -aux_z_min, -hd_center], + [-v_one_half, -v_one_half, -aux_column_width], + [-sep_distance, -v_one_half, -aux_column_width], + [-y_min_r, -v_one_half, -y_min_r], + [-v_one_half, -sep_distance, -aux_column_width], + [-hd_center, -hd_center, -aux_column_width], + [-aux_y_min, -hd_center, -aux_y_min], + [-v_one_half, -x_min_r, -x_min_r], + [-hd_center, -aux_x_min, -aux_x_min], + [-center_r, -center_r, -center_r], + ] + ) + + center_point + ) - x_min_y_min_z_max = _np.array( - [ - [-0.5, -0.5, aux_column_width], - [-seperator_distance, -0.5, aux_column_width], - [-y_min_r, -0.5, y_min_r], - [-0.5, -seperator_distance, aux_column_width], - [-hd_center, -hd_center, aux_column_width], - [-aux_y_min, -hd_center, aux_y_min], - [-0.5, -x_min_r, x_min_r], - [-hd_center, -aux_x_min, aux_x_min], - [-center_r, -center_r, center_r], - [-0.5, -0.5, seperator_distance], - [-seperator_distance, -0.5, seperator_distance], - [-aux_column_width, -0.5, seperator_distance], - [-0.5, -seperator_distance, seperator_distance], - [-hd_center, -hd_center, hd_center], - [-aux_column_width, -hd_center, hd_center], - [-0.5, -aux_column_width, seperator_distance], - [-hd_center, -aux_column_width, hd_center], - [-aux_z_max, -aux_z_max, hd_center], - [-0.5, -0.5, 0.5], - [-seperator_distance, -0.5, 0.5], - [-aux_column_width, -0.5, 0.5], - [-0.5, -seperator_distance, 0.5], - [-seperator_distance, -seperator_distance, 0.5], - [-aux_column_width, -seperator_distance, 0.5], - [-0.5, -aux_column_width, 0.5], - [-seperator_distance, -aux_column_width, 0.5], - [-z_max_r, -z_max_r, 0.5], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 2], control_points=x_min_y_min_z_max) - ) + x_max_y_min_z_min = ( + _np.array( + [ + [aux_column_width, -v_one_half, -v_one_half], + [sep_distance, -v_one_half, -v_one_half], + [v_one_half, -v_one_half, -v_one_half], + [aux_column_width, -sep_distance, -v_one_half], + [sep_distance, -sep_distance, -v_one_half], + [v_one_half, -sep_distance, -v_one_half], + [z_min_r, -z_min_r, -v_one_half], + [sep_distance, -aux_column_width, -v_one_half], + [v_one_half, -aux_column_width, -v_one_half], + [aux_column_width, -v_one_half, -sep_distance], + [sep_distance, -v_one_half, -sep_distance], + [v_one_half, -v_one_half, -sep_distance], + [aux_column_width, -hd_center, -hd_center], + [hd_center, -hd_center, -hd_center], + [v_one_half, -sep_distance, -sep_distance], + [aux_z_min, -aux_z_min, -hd_center], + [hd_center, -aux_column_width, -hd_center], + [v_one_half, -aux_column_width, -sep_distance], + [y_min_r, -v_one_half, -y_min_r], + [sep_distance, -v_one_half, -aux_column_width], + [v_one_half, -v_one_half, -aux_column_width], + [aux_y_min, -hd_center, -aux_y_min], + [hd_center, -hd_center, -aux_column_width], + [v_one_half, -sep_distance, -aux_column_width], + [center_r, -center_r, -center_r], + [hd_center, -aux_x_max, -aux_x_max], + [v_one_half, -x_max_r, -x_max_r], + ] + ) + + center_point + ) - x_max_y_min_z_max = _np.array( - [ - [y_min_r, -0.5, y_min_r], - [seperator_distance, -0.5, aux_column_width], - [0.5, -0.5, aux_column_width], - [aux_y_min, -hd_center, aux_y_min], - [hd_center, -hd_center, aux_column_width], - [0.5, -seperator_distance, aux_column_width], - [center_r, -center_r, center_r], - [hd_center, -aux_x_max, aux_x_max], - [0.5, -x_max_r, x_max_r], - [aux_column_width, -0.5, seperator_distance], - [seperator_distance, -0.5, seperator_distance], - [0.5, -0.5, seperator_distance], - [aux_column_width, -hd_center, hd_center], - [hd_center, -hd_center, hd_center], - [0.5, -seperator_distance, seperator_distance], - [aux_z_max, -aux_z_max, hd_center], - [hd_center, -aux_column_width, hd_center], - [0.5, -aux_column_width, seperator_distance], - [aux_column_width, -0.5, 0.5], - [seperator_distance, -0.5, 0.5], - [0.5, -0.5, 0.5], - [aux_column_width, -seperator_distance, 0.5], - [seperator_distance, -seperator_distance, 0.5], - [0.5, -seperator_distance, 0.5], - [z_max_r, -z_max_r, 0.5], - [seperator_distance, -aux_column_width, 0.5], - [0.5, -aux_column_width, 0.5], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 2], control_points=x_max_y_min_z_max) - ) + x_min_y_max_z_min = ( + _np.array( + [ + [-v_one_half, aux_column_width, -v_one_half], + [-sep_distance, aux_column_width, -v_one_half], + [-z_min_r, z_min_r, -v_one_half], + [-v_one_half, sep_distance, -v_one_half], + [-sep_distance, sep_distance, -v_one_half], + [-aux_column_width, sep_distance, -v_one_half], + [-v_one_half, v_one_half, -v_one_half], + [-sep_distance, v_one_half, -v_one_half], + [-aux_column_width, v_one_half, -v_one_half], + [-v_one_half, aux_column_width, -sep_distance], + [-hd_center, aux_column_width, -hd_center], + [-aux_z_min, aux_z_min, -hd_center], + [-v_one_half, sep_distance, -sep_distance], + [-hd_center, hd_center, -hd_center], + [-aux_column_width, hd_center, -hd_center], + [-v_one_half, v_one_half, -sep_distance], + [-sep_distance, v_one_half, -sep_distance], + [-aux_column_width, v_one_half, -sep_distance], + [-v_one_half, x_min_r, -x_min_r], + [-hd_center, aux_x_min, -aux_x_min], + [-center_r, center_r, -center_r], + [-v_one_half, sep_distance, -aux_column_width], + [-hd_center, hd_center, -aux_column_width], + [-aux_y_max, hd_center, -aux_y_max], + [-v_one_half, v_one_half, -aux_column_width], + [-sep_distance, v_one_half, -aux_column_width], + [-y_max_r, v_one_half, -y_max_r], + ] + ) + + center_point + ) - x_min_y_max_z_max = _np.array( - [ - [-0.5, x_min_r, x_min_r], - [-hd_center, aux_x_min, aux_x_min], - [-center_r, center_r, center_r], - [-0.5, seperator_distance, aux_column_width], - [-hd_center, hd_center, aux_column_width], - [-aux_y_max, hd_center, aux_y_max], - [-0.5, 0.5, aux_column_width], - [-seperator_distance, 0.5, aux_column_width], - [-y_max_r, 0.5, y_max_r], - [-0.5, aux_column_width, seperator_distance], - [-hd_center, aux_column_width, hd_center], - [-aux_z_max, aux_z_max, hd_center], - [-0.5, seperator_distance, seperator_distance], - [-hd_center, hd_center, hd_center], - [-aux_column_width, hd_center, hd_center], - [-0.5, 0.5, seperator_distance], - [-seperator_distance, 0.5, seperator_distance], - [-aux_column_width, 0.5, seperator_distance], - [-0.5, aux_column_width, 0.5], - [-seperator_distance, aux_column_width, 0.5], - [-z_max_r, z_max_r, 0.5], - [-0.5, seperator_distance, 0.5], - [-seperator_distance, seperator_distance, 0.5], - [-aux_column_width, seperator_distance, 0.5], - [-0.5, 0.5, 0.5], - [-seperator_distance, 0.5, 0.5], - [-aux_column_width, 0.5, 0.5], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 2], control_points=x_min_y_max_z_max) - ) + x_max_y_max_z_min = ( + _np.array( + [ + [z_min_r, z_min_r, -v_one_half], + [sep_distance, aux_column_width, -v_one_half], + [v_one_half, aux_column_width, -v_one_half], + [aux_column_width, sep_distance, -v_one_half], + [sep_distance, sep_distance, -v_one_half], + [v_one_half, sep_distance, -v_one_half], + [aux_column_width, v_one_half, -v_one_half], + [sep_distance, v_one_half, -v_one_half], + [v_one_half, v_one_half, -v_one_half], + [aux_z_min, aux_z_min, -hd_center], + [hd_center, aux_column_width, -hd_center], + [v_one_half, aux_column_width, -sep_distance], + [aux_column_width, hd_center, -hd_center], + [hd_center, hd_center, -hd_center], + [v_one_half, sep_distance, -sep_distance], + [aux_column_width, v_one_half, -sep_distance], + [sep_distance, v_one_half, -sep_distance], + [v_one_half, v_one_half, -sep_distance], + [center_r, center_r, -center_r], + [hd_center, aux_x_max, -aux_x_max], + [v_one_half, x_max_r, -x_max_r], + [aux_y_max, hd_center, -aux_y_max], + [hd_center, hd_center, -aux_column_width], + [v_one_half, sep_distance, -aux_column_width], + [y_max_r, v_one_half, -y_max_r], + [sep_distance, v_one_half, -aux_column_width], + [v_one_half, v_one_half, -aux_column_width], + ] + ) + + center_point + ) - x_max_y_max_z_max = _np.array( - [ - [center_r, center_r, center_r], - [hd_center, aux_x_max, aux_x_max], - [0.5, x_max_r, x_max_r], - [aux_y_max, hd_center, aux_y_max], - [hd_center, hd_center, aux_column_width], - [0.5, seperator_distance, aux_column_width], - [y_max_r, 0.5, y_max_r], - [seperator_distance, 0.5, aux_column_width], - [0.5, 0.5, aux_column_width], - [aux_z_max, aux_z_max, hd_center], - [hd_center, aux_column_width, hd_center], - [0.5, aux_column_width, seperator_distance], - [aux_column_width, hd_center, hd_center], - [hd_center, hd_center, hd_center], - [0.5, seperator_distance, seperator_distance], - [aux_column_width, 0.5, seperator_distance], - [seperator_distance, 0.5, seperator_distance], - [0.5, 0.5, seperator_distance], - [z_max_r, z_max_r, 0.5], - [seperator_distance, aux_column_width, 0.5], - [0.5, aux_column_width, 0.5], - [aux_column_width, seperator_distance, 0.5], - [seperator_distance, seperator_distance, 0.5], - [0.5, seperator_distance, 0.5], - [aux_column_width, 0.5, 0.5], - [seperator_distance, 0.5, 0.5], - [0.5, 0.5, 0.5], - ] - ) + _np.array([0.5, 0.5, 0.5]) - - spline_list.append( - _Bezier(degrees=[2, 2, 2], control_points=x_max_y_max_z_max) - ) - return (spline_list, None) + x_min_y_min_z_max = ( + _np.array( + [ + [-v_one_half, -v_one_half, aux_column_width], + [-sep_distance, -v_one_half, aux_column_width], + [-y_min_r, -v_one_half, y_min_r], + [-v_one_half, -sep_distance, aux_column_width], + [-hd_center, -hd_center, aux_column_width], + [-aux_y_min, -hd_center, aux_y_min], + [-v_one_half, -x_min_r, x_min_r], + [-hd_center, -aux_x_min, aux_x_min], + [-center_r, -center_r, center_r], + [-v_one_half, -v_one_half, sep_distance], + [-sep_distance, -v_one_half, sep_distance], + [-aux_column_width, -v_one_half, sep_distance], + [-v_one_half, -sep_distance, sep_distance], + [-hd_center, -hd_center, hd_center], + [-aux_column_width, -hd_center, hd_center], + [-v_one_half, -aux_column_width, sep_distance], + [-hd_center, -aux_column_width, hd_center], + [-aux_z_max, -aux_z_max, hd_center], + [-v_one_half, -v_one_half, v_one_half], + [-sep_distance, -v_one_half, v_one_half], + [-aux_column_width, -v_one_half, v_one_half], + [-v_one_half, -sep_distance, v_one_half], + [-sep_distance, -sep_distance, v_one_half], + [-aux_column_width, -sep_distance, v_one_half], + [-v_one_half, -aux_column_width, v_one_half], + [-sep_distance, -aux_column_width, v_one_half], + [-z_max_r, -z_max_r, v_one_half], + ] + ) + + center_point + ) + + x_max_y_min_z_max = ( + _np.array( + [ + [y_min_r, -v_one_half, y_min_r], + [sep_distance, -v_one_half, aux_column_width], + [v_one_half, -v_one_half, aux_column_width], + [aux_y_min, -hd_center, aux_y_min], + [hd_center, -hd_center, aux_column_width], + [v_one_half, -sep_distance, aux_column_width], + [center_r, -center_r, center_r], + [hd_center, -aux_x_max, aux_x_max], + [v_one_half, -x_max_r, x_max_r], + [aux_column_width, -v_one_half, sep_distance], + [sep_distance, -v_one_half, sep_distance], + [v_one_half, -v_one_half, sep_distance], + [aux_column_width, -hd_center, hd_center], + [hd_center, -hd_center, hd_center], + [v_one_half, -sep_distance, sep_distance], + [aux_z_max, -aux_z_max, hd_center], + [hd_center, -aux_column_width, hd_center], + [v_one_half, -aux_column_width, sep_distance], + [aux_column_width, -v_one_half, v_one_half], + [sep_distance, -v_one_half, v_one_half], + [v_one_half, -v_one_half, v_one_half], + [aux_column_width, -sep_distance, v_one_half], + [sep_distance, -sep_distance, v_one_half], + [v_one_half, -sep_distance, v_one_half], + [z_max_r, -z_max_r, v_one_half], + [sep_distance, -aux_column_width, v_one_half], + [v_one_half, -aux_column_width, v_one_half], + ] + ) + + center_point + ) + + x_min_y_max_z_max = ( + _np.array( + [ + [-v_one_half, x_min_r, x_min_r], + [-hd_center, aux_x_min, aux_x_min], + [-center_r, center_r, center_r], + [-v_one_half, sep_distance, aux_column_width], + [-hd_center, hd_center, aux_column_width], + [-aux_y_max, hd_center, aux_y_max], + [-v_one_half, v_one_half, aux_column_width], + [-sep_distance, v_one_half, aux_column_width], + [-y_max_r, v_one_half, y_max_r], + [-v_one_half, aux_column_width, sep_distance], + [-hd_center, aux_column_width, hd_center], + [-aux_z_max, aux_z_max, hd_center], + [-v_one_half, sep_distance, sep_distance], + [-hd_center, hd_center, hd_center], + [-aux_column_width, hd_center, hd_center], + [-v_one_half, v_one_half, sep_distance], + [-sep_distance, v_one_half, sep_distance], + [-aux_column_width, v_one_half, sep_distance], + [-v_one_half, aux_column_width, v_one_half], + [-sep_distance, aux_column_width, v_one_half], + [-z_max_r, z_max_r, v_one_half], + [-v_one_half, sep_distance, v_one_half], + [-sep_distance, sep_distance, v_one_half], + [-aux_column_width, sep_distance, v_one_half], + [-v_one_half, v_one_half, v_one_half], + [-sep_distance, v_one_half, v_one_half], + [-aux_column_width, v_one_half, v_one_half], + ] + ) + + center_point + ) + + x_max_y_max_z_max = ( + _np.array( + [ + [center_r, center_r, center_r], + [hd_center, aux_x_max, aux_x_max], + [v_one_half, x_max_r, x_max_r], + [aux_y_max, hd_center, aux_y_max], + [hd_center, hd_center, aux_column_width], + [v_one_half, sep_distance, aux_column_width], + [y_max_r, v_one_half, y_max_r], + [sep_distance, v_one_half, aux_column_width], + [v_one_half, v_one_half, aux_column_width], + [aux_z_max, aux_z_max, hd_center], + [hd_center, aux_column_width, hd_center], + [v_one_half, aux_column_width, sep_distance], + [aux_column_width, hd_center, hd_center], + [hd_center, hd_center, hd_center], + [v_one_half, sep_distance, sep_distance], + [aux_column_width, v_one_half, sep_distance], + [sep_distance, v_one_half, sep_distance], + [v_one_half, v_one_half, sep_distance], + [z_max_r, z_max_r, v_one_half], + [sep_distance, aux_column_width, v_one_half], + [v_one_half, aux_column_width, v_one_half], + [aux_column_width, sep_distance, v_one_half], + [sep_distance, sep_distance, v_one_half], + [v_one_half, sep_distance, v_one_half], + [aux_column_width, v_one_half, v_one_half], + [sep_distance, v_one_half, v_one_half], + [v_one_half, v_one_half, v_one_half], + ] + ) + + center_point + ) + + # Append the control points to the spline list + for control_points, degrees in [ + (x_min_y_min, [2, 2, 1]), + (x_max_y_min, [2, 2, 1]), + (x_min_y_max, [2, 2, 1]), + (x_max_y_max, [2, 2, 1]), + (x_min_z_min, [2, 1, 2]), + (x_max_z_min, [2, 1, 2]), + (x_min_z_max, [2, 1, 2]), + (x_max_z_max, [2, 1, 2]), + (y_min_z_min, [1, 2, 2]), + (y_max_z_min, [1, 2, 2]), + (y_min_z_max, [1, 2, 2]), + (y_max_z_max, [1, 2, 2]), + (x_min_y_min_z_min, [2, 2, 2]), + (x_max_y_min_z_min, [2, 2, 2]), + (x_min_y_max_z_min, [2, 2, 2]), + (x_max_y_max_z_min, [2, 2, 2]), + (x_min_y_min_z_max, [2, 2, 2]), + (x_max_y_min_z_max, [2, 2, 2]), + (x_min_y_max_z_max, [2, 2, 2]), + (x_max_y_max_z_max, [2, 2, 2]), + ]: + spline_list.append( + _Bezier(degrees=degrees, control_points=control_points) + ) + + if i_derivative == 0: + splines = spline_list.copy() + else: + derivatives.append(spline_list) + return (splines, derivatives) diff --git a/splinepy/microstructure/tiles/snappy.py b/splinepy/microstructure/tiles/snappy.py index dac00bd20..24a910e0d 100644 --- a/splinepy/microstructure/tiles/snappy.py +++ b/splinepy/microstructure/tiles/snappy.py @@ -8,6 +8,9 @@ class Snappy(_TileBase): """Snap-through tile consisting of a thin truss and a thick truss that collide into each other. + # TODO: currently the tile parameters are not implemented as the parameters variable + therefore, no parameter sensitivities are calculated + .. raw:: html

Fullscreen.

@@ -20,10 +23,16 @@ class Snappy(_TileBase): # dummy values - not used _evaluation_points = _np.array([[0.5, 0.5]]) _n_info_per_eval_point = 1 + _sensitivities_implemented = False + _closure_directions = ["y_min", "y_max"] + _parameter_bounds = [] + _parameters_shape = () + + _CONTACT_LENGTH_BOUNDS = [0.0, 0.5] def _closing_tile( self, - parameters=None, + parameters=None, # noqa: ARG002 parameter_sensitivities=None, # TODO closure=None, contact_length=0.1, @@ -61,8 +70,6 @@ def _closing_tile( if closure is None: raise ValueError("No closing direction given") - self.check_params(parameters) - if parameter_sensitivities is not None: raise NotImplementedError( "Derivatives are not implemented for this tile yet" @@ -220,7 +227,6 @@ def _closing_tile( spline_list.append( _Bezier(degrees=[3, 1], control_points=spline_10) ) - return spline_list elif closure == "y_max": spline_1 = _np.array( [ @@ -286,12 +292,13 @@ def _closing_tile( spline_list.append( _Bezier(degrees=[3, 1], control_points=spline_5) ) - return (spline_list, None) else: - raise ValueError( + raise NotImplementedError( "Closing tile is only implemented for y-enclosure" ) + return (spline_list, None) + def create_tile( self, parameters=None, @@ -339,13 +346,16 @@ def create_tile( """ for param in [a, b, c, r, contact_length]: - if not isinstance(param, float): - raise ValueError(f"Invalid Type, {param} is not float") + if not isinstance(param, (int, float)): + raise TypeError( + f"Invalid Type, {param} is neither int nor float" + ) if param < 0: raise ValueError("Invalid parameter, must be > 0.") - if not ((contact_length > 0) and (contact_length < 0.49)): - raise ValueError("The length of a side must be in (0.01, 0.49)") + self._check_custom_parameter( + contact_length, "contact length", self._CONTACT_LENGTH_BOUNDS + ) # Check horizontal parameters if not ((r + contact_length) < 0.5): @@ -362,7 +372,7 @@ def create_tile( if parameters is not None: raise NotImplementedError( - "Parametriazation is not implemented for this tile yet" + "Parametrization is not implemented for this tile yet" ) if parameter_sensitivities is not None: diff --git a/splinepy/microstructure/tiles/tile_base.py b/splinepy/microstructure/tiles/tile_base.py index 0c4a64100..80df767a0 100644 --- a/splinepy/microstructure/tiles/tile_base.py +++ b/splinepy/microstructure/tiles/tile_base.py @@ -6,12 +6,39 @@ class TileBase(_SplinepyBase): """ Base class for tile objects + + Attributes + --------------- + _dim: int + Dimension in physical space + _para_dim: int + Dimension in parametric space + _evaluation_points: np.ndarray (2D) + Points in parametric space where tile parameters are evaluated. Each parameter + is attributed one evaluation point. This is used for the parametrization spline. + _n_info_per_eval_point: int + Number of tile parameters per evaluation point + _sensitivities_implemented: bool + Whether sensitivities w.r.t. tile parameters are implemented + _closure_directions: list + List of directions in which the closure has been implemented + _parameter_bounds: list> + List of bounds for the tile parameters + _parameters_shape: tuple + Shape of parameters array + _default_parameter_value: float/np.ndarray + Default values for all tile parameters """ _dim = None _para_dim = None _evaluation_points = None _n_info_per_eval_point = None + _sensitivities_implemented = None + _closure_directions = None + _parameter_bounds = None + _parameters_shape = None + _default_parameter_value = None def __init__(self): if type(self) is TileBase: @@ -25,8 +52,7 @@ def _raise_if_not_set_else_return(cls, attr_name): attr = getattr(cls, attr_name, None) if attr is None and cls is not TileBase: raise NotImplementedError( - f"Inherited Tile-types need to provide {attr_name}, see " - "documentation." + f"Inherited Tile-types need to provide {attr_name}, see documentation." ) return attr @@ -46,7 +72,21 @@ def evaluation_points(self): return self._raise_if_not_set_else_return("_evaluation_points") @property - def dim(self): + def n_info_per_eval_point(cls): + """Number of parameters per evaluation point + + Parameters + ---------- + None + + Returns + ------- + n_info : int + """ + return cls._raise_if_not_set_else_return("_n_info_per_eval_point") + + @property + def dim(cls): """Returns dimensionality in physical space of the Microtile. Parameters @@ -57,7 +97,7 @@ def dim(self): ------- dim : int """ - return self._raise_if_not_set_else_return("_dim") + return cls._raise_if_not_set_else_return("_dim") @property def para_dim(self): @@ -73,8 +113,82 @@ def para_dim(self): """ return self._raise_if_not_set_else_return("_para_dim") - def check_params(self, params): - """Checks if the parameters have the correct format and shape + @property + def sensitivities_implemented(cls): + """Returns whether sensitivities are implemented for the microtile + + Parameters + ---------- + None + + Returns + ------- + is_implemented: bool + """ + return cls._raise_if_not_set_else_return("_sensitivities_implemented") + + @property + def closure_directions(cls): + """Returns the available closure directions of the microtile + + Parameters + ---------- + None + + Returns + ------- + directions: None/list + """ + return cls._closure_directions + + @property + def parameter_bounds(self): + """Returns the bounds for the microtiles' parameters. + + Depending on the tile, parameter bounds can dynamically change (e.g. Cross2D). + Therefore, it is instance-dependent and self instead of cls is used. + + Parameters + ---------- + None + + Returns + ------- + bounds: list> + """ + return self._raise_if_not_set_else_return("_parameter_bounds") + + @property + def parameters_shape(cls): + """Returns the shape of the microtile's parameters array + + Parameters + ---------- + None + + Returns + ------- + shape: tuple + """ + return cls._raise_if_not_set_else_return("_parameters_shape") + + @property + def default_parameter_value(cls): + """Returns the default value of the microtile's parameters + + Parameters + ---------- + None + + Returns + ------- + default_value: float/None + """ + return cls._default_parameter_value + + def check_params(self, parameters): + """Checks if the parameters have the correct format and shape and are within + defined bounds Parameters ---------- @@ -85,14 +199,14 @@ def check_params(self, params): ------- True: Boolean """ - # check if tuple - - if not (isinstance(params, _np.ndarray) and params.ndim == 2): + # Check correct format + if not (isinstance(parameters, _np.ndarray) and parameters.ndim == 2): raise TypeError("parameters must be two-dimensional np array") + # Check correct shape if not ( - (self._evaluation_points.shape[0] == params.shape[0]) - and (self._n_info_per_eval_point == params.shape[1]) + (self._evaluation_points.shape[0] == parameters.shape[0]) + and (self._n_info_per_eval_point == parameters.shape[1]) ): raise TypeError( f"Mismatch in parameter size, expected " @@ -100,6 +214,21 @@ def check_params(self, params): f"{self._n_info_per_eval_point}" ) + # Check if all parameters are within bounds + if self._parameter_bounds is not None: + bounds = _np.array(self._parameter_bounds) + lower_bounds = bounds[:, 0] + upper_bounds = bounds[:, 1] + within_bounds = (parameters.ravel() > lower_bounds) & ( + parameters.ravel() < upper_bounds + ) + if not _np.all(within_bounds): + out_of_bounds = parameters[~within_bounds.reshape(parameters.shape)] + raise ValueError( + f"The following parameters are out of bounds: {out_of_bounds}. " + f"Expected bounds: lower: {lower_bounds} and upper: {upper_bounds}" + ) + return True def check_param_derivatives(self, derivatives): @@ -117,9 +246,7 @@ def check_param_derivatives(self, derivatives): if derivatives is None: return False - if not ( - isinstance(derivatives, _np.ndarray) and derivatives.ndim == 3 - ): + if not (isinstance(derivatives, _np.ndarray) and derivatives.ndim == 3): raise TypeError("parameters must be three-dimensional np array") if not ( @@ -150,6 +277,74 @@ def create_tile(self, **kwargs): List of list of splines that represents parameter sensitivities. If it is not implemented, returns None. """ - raise NotImplementedError( - f"create_tile() not implemented for {type(self)}" - ) + raise NotImplementedError(f"create_tile() not implemented for {type(self)}") + + def _process_input(self, parameters, parameter_sensitivities): + """Processing input for create_tile and _closing_tile + + Parameters + ------------- + parameters: np.ndarray + Tile parameters + parameter_sensitivities: np.ndarray + Tile parameter sensitivities to be calculated + + Returns + --------- + parameters: np.ndarray + Tile parameters + n_derivatives: np.ndarray + Number of different derivatives to compute + derivatives: list/None + Initialized list of derivatives + """ + # Set parameters to default values if not user-given + if parameters is None: + default_value = self.default_parameter_value + self._logd(f"Setting parameters to default values ({default_value})") + if isinstance(default_value, float): + parameters = _np.full( + (len(self.evaluation_points), self.n_info_per_eval_point), + default_value, + ) + elif isinstance(default_value, _np.ndarray): + parameters = default_value + + # Validity check of parameters and their sensitivities + self.check_params(parameters) + self.check_param_derivatives(parameter_sensitivities) + + # Initialize list of derivatives + if parameter_sensitivities is not None: + n_derivatives = parameter_sensitivities.shape[2] + derivatives = [] + else: + n_derivatives = 0 + derivatives = None + + return parameters, n_derivatives, derivatives + + def _check_custom_parameter(self, value, param_name, bounds): + """Check if a custom tile parameter (e.g. contact length) is within bounds + + Parameters + ------------------ + value: float + Value of the custom parameter + param_name: str + Name of custom parameter + bounds: list + List of min. and max. bound + """ + + if not isinstance(bounds, list): + raise TypeError("bounds has to be a list") + if len(bounds) != 2: + raise ValueError("Bounds must consist of a min. and a max. value") + min_bound, max_bound = bounds + + if not isinstance(value, (int, float)): + raise ValueError(f"Invalid type for {param_name}") + + if not ((value > min_bound) and (value < max_bound)): + raise ValueError(f"{param_name} must be in ({min_bound}, {max_bound})") diff --git a/splinepy/utils/data.py b/splinepy/utils/data.py index 4c66e716d..00105c796 100644 --- a/splinepy/utils/data.py +++ b/splinepy/utils/data.py @@ -468,7 +468,7 @@ def uniform_query(bounds, resolutions): # create per-dimension queries queries_per_dim = [] - for lb, ub, r in zip(lower_b, upper_b, resolutions): + for lb, ub, r in zip(lower_b, upper_b, resolutions, strict=True): queries_per_dim.append(_np.linspace(lb, ub, r)) return cartesian_product(queries_per_dim, reverse=True) @@ -573,8 +573,7 @@ def __init__( # can call sample or has a function? if not self.has_function and not self.is_spline: raise ValueError( - "None spline data should at least have an accompanying " - "function." + "None spline data should at least have an accompanying function." ) def as_vertex_data(self, resolutions=None, on=None): @@ -595,8 +594,7 @@ def as_vertex_data(self, resolutions=None, on=None): if self.has_locations and (resolutions is not None or on is not None): raise ValueError( - "Location dependent data can't be evaluated with `resolutions`" - " or `at`." + "Location dependent data can't be evaluated with `resolutions` or `at`." ) # if resolutions is specified, this is not a location query diff --git a/tests/conftest.py b/tests/conftest.py index 5043ef971..c1068c716 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,7 +15,7 @@ def error_log(*args): @pytest.fixture def np_rng(): - return np.random.default_rng() + return np.random.default_rng(seed=0) # query points @@ -41,6 +41,52 @@ def queries_3D(): ] +# hard-coded values to keep the same for derivative/sensitivity calculations +@pytest.fixture +def h_eps(): + """ + Perturbation/step size for finite difference evaluation of derivative/sensitivity. + + The value 1e-5 is arbitrary, but is a compromise between: + - Being small enough to ensure the finite difference calculation being accurate + - Being large enough to avoid round-off error in floating-point arithmetic + """ + return 1e-5 + + +@pytest.fixture +def n_test_points(): + """ + Number of random testing points (in parametric domain) + + The number 10 is arbitrary and should ensure to have good test coverage. Increasing + this number could yield more thorough tests at the cost of longer runtime. + """ + return 10 + + +@pytest.fixture +def big_perturbation(): + """Value for perturbation of parameters value + + The number 0.1 is chosen arbitrarily. This value is for testing out of bounds + parameter values. It is designed to add to the maximum or subtract from the + minimum bound to deliberately make the values out of the bounds.""" + return 0.1 + + +@pytest.fixture +def fd_derivative_stepsizes_and_weights(): + """Stepsizes and weights for the calculation of the derivative using finite + differences. Using fourth-order accurate centered scheme. + + Returns + ------- + stepsizes_and_weights, denominator: dict + """ + return {-2: 1 / 12, -1: -8 / 12, 1: 8 / 12, 2: -1 / 12} + + # initializing a spline should be a test itself, so provide `dict_spline` # this is "iga-book"'s fig 2.15. @pytest.fixture @@ -222,7 +268,9 @@ def _raster(bounds, resolutions): pts = np.meshgrid( *[ np.linspace(lo, up, re) - for lo, up, re in zip(l_bounds, u_bounds, resolutions) + for lo, up, re in zip( + l_bounds, u_bounds, resolutions, strict=True + ) ], indexing="ij", ) @@ -270,7 +318,7 @@ def _are_splines_equal(a, b, print_=False): return False for req_prop in a.required_properties: if req_prop == "knot_vectors": - for aa, bb in zip(a.knot_vectors, b.knot_vectors): + for aa, bb in zip(a.knot_vectors, b.knot_vectors, strict=True): if not np.allclose(aa.numpy(), bb.numpy()): if print_: error_log("a.kvs", a.kvs) @@ -292,7 +340,7 @@ def _are_items_close(a, b): """returns True if items in a and b are close""" all_close = True - for i, (aa, bb) in enumerate(zip(a, b)): + for i, (aa, bb) in enumerate(zip(a, b, strict=True)): if not all(np.isclose(aa, bb)): # print to inform error_log(f"elements in index-{i} are not close") @@ -312,7 +360,7 @@ def _are_items_same(a, b): """returns True if items in a and b are same""" all_same = True - for i, (aa, bb) in enumerate(zip(a, b)): + for i, (aa, bb) in enumerate(zip(a, b, strict=True)): if aa != bb: # print to inform error_log(f"element in index-{i} are not same") @@ -332,8 +380,7 @@ def _are_stripped_lines_same(a, b, ignore_order=False): """returns True if items in a and b same, preceding and tailing whitespaces are ignored and strings are joined""" all_same = True - - for i, (line_a, line_b) in enumerate(zip(a, b)): + for i, (line_a, line_b) in enumerate(zip(a, b, strict=True)): # check stripped string stripped_a, stripped_b = line_a.strip(), line_b.strip() diff --git a/tests/data/mfem_cartesian_2d.mesh b/tests/data/mfem_cartesian_2d.mesh index b70d4a28a..746ffe2b6 100644 --- a/tests/data/mfem_cartesian_2d.mesh +++ b/tests/data/mfem_cartesian_2d.mesh @@ -85,3 +85,4 @@ controlpoints_cartesian 2.0 0.0 1.0 1.0 1.0 1.0 2.0 1.0 1.0 + diff --git a/tests/data/mfem_cartesian_3d.mesh b/tests/data/mfem_cartesian_3d.mesh index 389d3928d..b6bc475cb 100644 --- a/tests/data/mfem_cartesian_3d.mesh +++ b/tests/data/mfem_cartesian_3d.mesh @@ -147,3 +147,4 @@ controlpoints_cartesian 2.0 0.0 1.0 1.0 1.0 1.0 1.0 1.0 2.0 1.0 1.0 1.0 + diff --git a/tests/helpme/test_create.py b/tests/helpme/test_create.py index 265f3c103..8ec877be2 100644 --- a/tests/helpme/test_create.py +++ b/tests/helpme/test_create.py @@ -1,5 +1,6 @@ import numpy as np import pytest +from gustaf.utils import arr import splinepy @@ -181,7 +182,7 @@ def check_parametric_view(spline, conform): # same knot_vectors if spline.has_knot_vectors: - for p_kv, kv in zip(p_spl.kvs, spline.kvs): + for p_kv, kv in zip(p_spl.kvs, spline.kvs, strict=True): assert np.allclose(p_kv, kv) # same weights @@ -193,7 +194,9 @@ def check_parametric_view(spline, conform): assert not any(p_spl.ds - 1) # same unique knots - implies same p_bounds - for p_ukv, ukv in zip(p_spl.unique_knots, spline.unique_knots): + for p_ukv, ukv in zip( + p_spl.unique_knots, spline.unique_knots, strict=True + ): assert np.allclose(p_ukv, ukv) spl = request.getfixturevalue(splinetype) @@ -343,3 +346,308 @@ def test_determinant_spline( det_spl.evaluate(queries=rnd_queries).ravel(), np.linalg.det(sp_i.jacobian(queries=rnd_queries)), ), f"{sp_i.whatami} at index {idx} failed determinant spline" + + +### TESTS FOR SWEEPING ### + + +# test the basic functionality of the swept-function +def test_swept_basic_functionality(): + cross_section = splinepy.NURBS( + degrees=[2, 1], + knot_vectors=[ + [0, 0, 0, 1, 1, 1], + [0, 0, 1, 1], + ], + control_points=[ + [-1.0, 0.0], + [-1.0, 1.0], + [0.0, 1.0], + [-2.0, 0.0], + [-2.0, 2.0], + [0.0, 2.0], + ], + weights=[ + [1.0], + [2**-0.5], + [1.0], + [1.0], + [2**-0.5], + [1.0], + ], + ) + trajectory = splinepy.BSpline( + degrees=[3], + control_points=[[0, 0], [1, 2], [2, 3], [3, 3]], + knot_vectors=[[0, 0, 0, 0, 1, 1, 1, 1]], + ) + + # create result --> 3D body should be created + result = splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=trajectory, + anchor="parametric", + ) + + # test result's type + assert result is not None + assert result.is_rational + + # test invalid input + invalid_trajectory = "invalid_trajectory" + with pytest.raises(TypeError): + splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=invalid_trajectory, + anchor="auto", + ) + + # test result's shape + assert ( + result.control_points.shape[0] + == cross_section.control_points.shape[0] + * trajectory.control_points.shape[0] + ) + + # test if result is 3D + assert result.dim == 3 + + # test if result's degrees are correct + assert np.allclose( + result.degrees, + [ + cross_section.degrees[0], + cross_section.degrees[1], + trajectory.degrees[0], + ], + ) + + # test if result's knot vectors are correct + assert np.allclose( + result.knot_vectors[0], cross_section.knot_vectors[0] + ), "Knot vector of first cross-section dimension does not match." + assert np.allclose( + result.knot_vectors[1], cross_section.knot_vectors[1] + ), "Knot vector of second cross-section dimension does not match." + assert np.allclose( + result.knot_vectors[2], trajectory.knot_vectors[0] + ), "Knot vector of trajectory does not match." + + +# create linear swept surface and compare it to extruded surface +def test_swept_to_extruded(): + + cross_section = splinepy.NURBS( + degrees=[2], + control_points=[[-0.5, -1 / 3], [0, 2 / 3], [0.5, -1 / 3]], + weights=[1, 0.5, 1], + knot_vectors=[[0, 0, 0, 1, 1, 1]], + ) + trajectory = splinepy.NURBS( + degrees=[1], + control_points=[[0, 0, 0], [0, 0, 3]], + weights=[1, 1], + knot_vectors=[[0, 0, 1, 1]], + ) + + result = splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=trajectory, + anchor="parametric", + rotation_adaption=np.pi / 2, + ) + extruded_result = splinepy.helpme.create.extruded(cross_section, [0, 0, 3]) + assert np.allclose(result.control_points, extruded_result.control_points) + + +# check if cross-section's control points are always placed in the correct angle +def test_swept_control_point_placing(np_rng): + + cross_section = splinepy.BSpline( + degrees=[2], + control_points=[[0, 0], [0.5, 1], [1, 0]], + knot_vectors=[[0, 0, 0, 1, 1, 1]], + ) + + trajectory = splinepy.BSpline( + degrees=[3], + control_points=[[0, 0, 0], [1, 0, 1], [0, 0, 2], [0, 0, 3]], + knot_vectors=[[0, 0, 0, 0, 1, 1, 1, 1]], + ) + result = splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=trajectory, + anchor="parametric", + ) + + def calculate_cs_normal_vector(cs_cp, res_cp, rand): + + # take the control points of the chosen cross-section + taken_cs_cp = res_cp[ + rand * len(cs_cp) : rand * len(cs_cp) + len(cs_cp) + ] + P1 = taken_cs_cp[0] + P2 = taken_cs_cp[1] + P3 = taken_cs_cp[2] + + # calculate normal vector of cross-section + v1 = P2 - P1 + v2 = P3 - P1 + return np.cross(v1, v2) + + cs_cp = cross_section.control_points + res_cp = result.control_points + + # choose id of cross-section to take from result + rand = np_rng.integers(0, len(res_cp) / len(cs_cp), 1)[0] + + normal_vector = calculate_cs_normal_vector(cs_cp, res_cp, rand) + + # evaluate trajectory parameter value at the position of the cross-section + rand_traj_pos = trajectory.greville_abscissae()[rand] + # calculate tangent vector of trajectory at the position of the cross-section + rand_traj_tangent = trajectory.derivative([rand_traj_pos], 1).ravel() + + # check if normal vector is parallel to tangent vector + # --> then transformation of cross-section is correct + assert np.allclose(np.cross(rand_traj_tangent, normal_vector), 0) + + ## TEST ALSO WITH CUSTOM NORMAL VECTOR ## + + # make sure that the angle between trajectory's tangent and cross-section's + # normal vector is always the same + custom_normal = np.array([1, 0, 1]) + + angle_of_custom_cs_normal = np.arctan2(custom_normal[2], custom_normal[0]) + # calculate rotation matrix for cross-section normal vector + R = arr.rotation_matrix_around_axis( + axis=[0, 1, 0], rotation=angle_of_custom_cs_normal, degree=False + ) + # in sweeping, the cross-section is rotated, we mimic this here + test_normal = np.matmul(R, custom_normal) + + start_traj_tang = trajectory.derivative([[0]], 1).ravel() + angle_at_start = np.arccos( + np.dot(start_traj_tang, test_normal) + / (np.linalg.norm(start_traj_tang) * np.linalg.norm(test_normal)) + ) + + result_with_cus_normal = splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=trajectory, + cross_section_normal=custom_normal, + anchor="auto", + ) + + result_with_cus_normal_cps = result_with_cus_normal.control_points + normal_vector_custom_version = ( + calculate_cs_normal_vector(cs_cp, result_with_cus_normal_cps, rand) + * -1 + ) + + angle_at_rand = np.arccos( + np.dot(rand_traj_tangent, normal_vector_custom_version) + / ( + np.linalg.norm(rand_traj_tangent) + * np.linalg.norm(normal_vector_custom_version) + ) + ) + + assert np.allclose(angle_at_start, angle_at_rand) + + +# check if cross-section's center lays on the trajectory +def test_swept_cross_section_centering(np_rng): + + cross_section = splinepy.BSpline( + degrees=[2], + control_points=[[0, 0], [0.5, 1], [1, 0]], + knot_vectors=[[0, 0, 0, 1, 1, 1]], + ) + trajectory = splinepy.BSpline( + degrees=[3], + control_points=[[0, 0, 0], [1, 2, 0], [2, 3, 0], [3, 3, 0]], + knot_vectors=[[0, 0, 0, 0, 1, 1, 1, 1]], + ) + result = splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=trajectory, + anchor="parametric", + ) + + # choose random parameter value of trajectory + r_par_val = np_rng.random(1) + # evaluate swept spline at the chosen position of the trajectory + # --> cross-section parameter value 0.5 ensures that the center of the + # cross-section is evaluated + coords = result.evaluate([[0.5, r_par_val[0]]]).ravel() + # evaluate trajectory at the chosen position of the trajectory + ref_coords = trajectory.evaluate([r_par_val]).ravel() + + assert np.allclose(coords, ref_coords) + + +def test_swept_anchor_modes(): + cross_section = splinepy.helpme.create.circle(0.55).nurbs + cross_section.control_points[:, 0] *= 1.2 + cross_section.control_points[:, 1] *= 0.8 + cross_section.control_points[1] += np.array([0.9, -0.15]) + cross_section.control_points[2] += np.array([0.45, 0.55]) + cross_section.control_points[5] += np.array([-0.35, -0.45]) + cross_section.weights[1] *= 0.25 + cross_section.weights[2] *= 0.45 + cross_section.weights[5] *= 0.5 + + trajectory = splinepy.BSpline( + degrees=[1], + control_points=[[0, 0, 0], [0, 0, 2]], + knot_vectors=[[0, 0, 1, 1]], + ) + + swept_parametric = splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=trajectory, + anchor="parametric", + ) + swept_control_box = splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=trajectory, + anchor="control_box", + ) + swept_geometry_box = splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=trajectory, + anchor="geometry_box", + ) + swept_auto = splinepy.helpme.create.swept( + cross_section=cross_section, + trajectory=trajectory, + anchor="auto", + ) + + assert np.allclose( + swept_auto.control_points, swept_geometry_box.control_points + ) + assert not np.allclose( + swept_parametric.control_points, swept_geometry_box.control_points + ) + assert not np.allclose( + swept_control_box.control_points, swept_geometry_box.control_points + ) + + n_section_cps = cross_section.control_points.shape[0] + diff_control_vs_geometry = ( + swept_control_box.control_points[:n_section_cps] + - swept_geometry_box.control_points[:n_section_cps] + ) + diff_parametric_vs_geometry = ( + swept_parametric.control_points[:n_section_cps] + - swept_geometry_box.control_points[:n_section_cps] + ) + + assert np.allclose(diff_control_vs_geometry, diff_control_vs_geometry[0]) + assert np.allclose( + diff_parametric_vs_geometry, diff_parametric_vs_geometry[0] + ) + assert not np.allclose(diff_control_vs_geometry[0], 0.0) + assert not np.allclose(diff_parametric_vs_geometry[0], 0.0) diff --git a/tests/helpme/test_integrate.py b/tests/helpme/test_integrate.py index 9dc287e63..ee2444c7f 100644 --- a/tests/helpme/test_integrate.py +++ b/tests/helpme/test_integrate.py @@ -34,20 +34,20 @@ def test_volume_integration_embedded(np_rng): Test volume integration for splines using numerical integration of the Jacobi-Determinant """ - # Test 1D -> 2D + # Test 1D -> 2D volume integration for Bezier expected_result = 2.0**1.5 bezier = splinepy.Bezier(degrees=[1], control_points=[[0, 0], [2, 2]]) assert np.allclose(bezier.integrate.volume(), expected_result) - # test for other types same spline + # For same curve, test other spline types assert np.allclose(bezier.bspline.integrate.volume(), expected_result) assert np.allclose( bezier.rationalbezier.integrate.volume(), expected_result ) assert np.allclose(bezier.nurbs.integrate.volume(), expected_result) - # Check if equal after refinement + # Check if volume is equal after degree elevation bezier.elevate_degrees([0, 0, 0]) assert np.allclose(bezier.integrate.volume(), expected_result) @@ -177,6 +177,7 @@ def test_assertions(np_rng): def test_function_integration(np_rng): col1_factor = 2 + # Define vector-valued constant function def volume_function(x): vf = np.ones((len(x), 2)) # scale it with a factor to get a different value @@ -186,12 +187,11 @@ def volume_function(x): bezier = splinepy.Bezier( degrees=[1, 2], control_points=np_rng.random((6, 2)) ) - + # Test function integration for constant functions assert np.allclose( [bezier.integrate.volume(), col1_factor * bezier.integrate.volume()], bezier.integrate.parametric_function(volume_function), ) - # try bsplines bspline = bezier.bspline assert np.allclose( @@ -200,6 +200,59 @@ def volume_function(x): ) +def test_transformation_class(np_rng): + """Test element transformation of single patch""" + # Create quadratic spline + spline = splinepy.BSpline( + degrees=[2, 2], + knot_vectors=[[0, 0, 0, 1, 1, 1], [0, 0, 0, 1, 1, 1]], + control_points=splinepy.utils.data.cartesian_product( + [np.linspace(0, 1, 3), np.linspace(0, 1, 3)] + ), + ) + + # Randomly insert knots along both parametric dimensions + spline.insert_knots(0, np_rng.random(2)) + spline.insert_knots(1, np_rng.random(2)) + + # Create transformation class for spline + trafo = splinepy.helpme.integrate.Transformation(spline) + + # Check whether element quadrature points lie inside element + ukvs = spline.unique_knots + trafo.compute_all_element_quad_points() + # Check quadrature points of each element + for element_id, quad_points in enumerate(trafo.all_quad_points): + grid_id = trafo.get_element_grid_id(element_id) + # Extract the corners of the current element + element_corners = [ + ukv[grid_dim_id : grid_dim_id + 2] + for ukv, grid_dim_id in zip(ukvs, grid_id, strict=True) + ] + # Check if quadrature points lie within element + for dim, corners in enumerate(element_corners): + assert np.all( + (quad_points[:, dim] > corners[0]) + & (quad_points[:, dim] < corners[1]) + ), f"Quadrature points do not lie within element for dimension {dim}" + + # For given spline, all Jacobians should be identity matrix + eye = np.eye(spline.para_dim) + trafo.compute_all_element_jacobian_inverses() + for element_jacobians in trafo.all_jacobians: + for jacobian_at_quad_point in element_jacobians: + assert np.allclose( + eye, jacobian_at_quad_point + ), "All Jacobians should be identity matrix" + + # For created spline, all determinants should equal one + trafo.compute_all_element_jacobian_determinants() + assert np.allclose( + trafo.all_jacobian_determinants, + np.ones_like(trafo.all_jacobian_determinants), + ), "All Jacobians' determinants should be equal to one" + + def test_physical_function_integration(np_rng): # Analytical integral of y*(5-y) over [0,1]x[0,5] rectangle integral_analytical = 125 / 6 diff --git a/tests/io/test_cats.py b/tests/io/test_cats.py index 46d835cbf..dd06cda7a 100644 --- a/tests/io/test_cats.py +++ b/tests/io/test_cats.py @@ -259,6 +259,7 @@ def test_cats_import(to_tmpf, are_splines_equal): for a, b in zip( multipatch_geometry, multipatch_geometry_loaded, + strict=True, ) ) @@ -276,5 +277,6 @@ def test_cats_import(to_tmpf, are_splines_equal): for a, b in zip( multipatch_geometry, multipatch_geometry_loaded, + strict=True, ) ) diff --git a/tests/io/test_gismo.py b/tests/io/test_gismo.py index 4376f41d0..80ae3531c 100644 --- a/tests/io/test_gismo.py +++ b/tests/io/test_gismo.py @@ -160,10 +160,13 @@ def test_gismo_export_2D( labeled_boundaries=False, ) - with open(tmpf) as tmp_read, open( - os.path.dirname(__file__) - + "/../data/gismo_noindent_nolabels_ascii_2d.xml" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(__file__) + + "/../data/gismo_noindent_nolabels_ascii_2d.xml" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) @@ -183,10 +186,13 @@ def test_gismo_export_2D_indented( labeled_boundaries=False, ) - with open(tmpf) as tmp_read, open( - os.path.dirname(__file__) - + "/../data/gismo_indent_nolabels_ascii_2d.xml" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(__file__) + + "/../data/gismo_indent_nolabels_ascii_2d.xml" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) @@ -207,10 +213,13 @@ def test_gismo_export_2D_labels( labeled_boundaries=True, ) - with open(tmpf) as tmp_read, open( - os.path.dirname(__file__) - + "/../data/gismo_noindent_labels_ascii_2d.xml" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(__file__) + + "/../data/gismo_noindent_labels_ascii_2d.xml" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) @@ -230,10 +239,13 @@ def test_gismo_export_2D_labels_indented( labeled_boundaries=True, ) - with open(tmpf) as tmp_read, open( - os.path.dirname(__file__) - + "/../data/gismo_indent_labels_ascii_2d.xml" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(__file__) + + "/../data/gismo_indent_labels_ascii_2d.xml" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) @@ -254,10 +266,13 @@ def test_gismo_export_3D( labeled_boundaries=False, ) - with open(tmpf) as tmp_read, open( - os.path.dirname(__file__) - + "/../data/gismo_noindent_nolabels_ascii_3d.xml" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(__file__) + + "/../data/gismo_noindent_nolabels_ascii_3d.xml" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) @@ -324,10 +339,13 @@ def test_gismo_export_additional_blocks( additional_blocks=additional_blocks.to_list(), ) - with open(tmpf) as tmp_read, open( - os.path.dirname(__file__) - + "/../data/gismo_additional_blocks.xml" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(__file__) + + "/../data/gismo_additional_blocks.xml" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) @@ -347,10 +365,13 @@ def test_gismo_export_3D_indented( labeled_boundaries=False, ) - with open(tmpf) as tmp_read, open( - os.path.dirname(__file__) - + "/../data/gismo_indent_nolabels_ascii_3d.xml" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(__file__) + + "/../data/gismo_indent_nolabels_ascii_3d.xml" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) @@ -399,6 +420,7 @@ def test_gismo_import(to_tmpf, are_splines_equal): for a, b in zip( multipatch_geometry.patches, multipatch_geometry_loaded.patches, + strict=True, ) ) @@ -425,6 +447,7 @@ def test_gismo_import(to_tmpf, are_splines_equal): for a, b in zip( multipatch_geometry.patches, multipatch_geometry_loaded.patches, + strict=True, ) ) @@ -505,6 +528,7 @@ def test_gismo_import_with_options(to_tmpf, are_splines_equal): for a, b in zip( multipatch_geometry.patches, multipatch_geometry_loaded.patches, + strict=True, ) ) @@ -552,10 +576,13 @@ def test_gismo_io_binary(to_tmpf, are_stripped_lines_same, are_splines_equal): gismo_options_loaded, ) = splinepy.io.gismo.load(tmpf, load_options=True) - with open(tmpf) as tmp_read, open( - os.path.dirname(os.path.dirname(__file__)) - + "/data/gismo_noindent_nolabels_b64_3d.xml" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(os.path.dirname(__file__)) + + "/data/gismo_noindent_nolabels_b64_3d.xml" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) @@ -565,6 +592,7 @@ def test_gismo_io_binary(to_tmpf, are_stripped_lines_same, are_splines_equal): for a, b in zip( multipatch_geometry.patches, multipatch_geometry_loaded.patches, + strict=True, ) ) diff --git a/tests/io/test_iges.py b/tests/io/test_iges.py index 7b3fb2be5..6aed23afb 100644 --- a/tests/io/test_iges.py +++ b/tests/io/test_iges.py @@ -56,5 +56,5 @@ def test_iges_round_trip_bsplines(to_tmpf, are_splines_equal): assert all( are_splines_equal(a, b) - for a, b in zip(splines_in_3d, list_of_splines_loaded) + for a, b in zip(splines_in_3d, list_of_splines_loaded, strict=True) ) diff --git a/tests/io/test_irit.py b/tests/io/test_irit.py index ea28d8c6e..be56418ec 100644 --- a/tests/io/test_irit.py +++ b/tests/io/test_irit.py @@ -25,5 +25,7 @@ def test_irit_export_import( list_of_splines_loaded = splinepy.io.irit.load(tmpf) assert all( are_splines_equal(a, b) - for a, b in zip(list_of_splines, list_of_splines_loaded) + for a, b in zip( + list_of_splines, list_of_splines_loaded, strict=True + ) ) diff --git a/tests/io/test_json.py b/tests/io/test_json.py index e4571655e..8016da122 100644 --- a/tests/io/test_json.py +++ b/tests/io/test_json.py @@ -43,7 +43,9 @@ def test_json_import(to_tmpf, are_splines_equal): list_of_splines_loaded = splinepy.io.json.load(tmpf) assert all( are_splines_equal(a, b) - for a, b in zip(list_of_splines, list_of_splines_loaded) + for a, b in zip( + list_of_splines, list_of_splines_loaded, strict=True + ) ) # Test Import export with non base64 encoding @@ -53,5 +55,7 @@ def test_json_import(to_tmpf, are_splines_equal): list_of_splines_loaded = splinepy.io.json.load(tmpf) assert all( are_splines_equal(a, b) - for a, b in zip(list_of_splines, list_of_splines_loaded) + for a, b in zip( + list_of_splines, list_of_splines_loaded, strict=True + ) ) diff --git a/tests/io/test_mfem_export.py b/tests/io/test_mfem_export.py index 5c2544a94..4617a340f 100644 --- a/tests/io/test_mfem_export.py +++ b/tests/io/test_mfem_export.py @@ -43,9 +43,12 @@ def test_mfem_export(to_tmpf, are_stripped_lines_same): tmpf, [bez_el0, bsp_el2, nur_el3, rbz_el1] ) - with open(tmpf) as tmp_read, open( - os.path.dirname(__file__) + "/../data/mfem_cartesian_2d.mesh" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(__file__) + "/../data/mfem_cartesian_2d.mesh" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) @@ -123,9 +126,12 @@ def test_mfem_export(to_tmpf, are_stripped_lines_same): tmpf, [bez_el0, bsp_el2, nur_el3, rbz_el1] ) - with open(tmpf) as tmp_read, open( - os.path.dirname(__file__) + "/../data/mfem_cartesian_3d.mesh" - ) as base_file: + with ( + open(tmpf) as tmp_read, + open( + os.path.dirname(__file__) + "/../data/mfem_cartesian_3d.mesh" + ) as base_file, + ): assert are_stripped_lines_same( base_file.readlines(), tmp_read.readlines(), True ) diff --git a/tests/io/test_npz.py b/tests/io/test_npz.py index 7a16f194e..600f14e47 100644 --- a/tests/io/test_npz.py +++ b/tests/io/test_npz.py @@ -43,5 +43,7 @@ def test_npz_io(to_tmpf, are_splines_equal): list_of_splines_loaded = splinepy.io.npz.load(tmpf + ".npz") assert all( are_splines_equal(a, b) - for a, b in zip(list_of_splines, list_of_splines_loaded) + for a, b in zip( + list_of_splines, list_of_splines_loaded, strict=True + ) ) diff --git a/tests/test_bezier_extraction.py b/tests/test_bezier_extraction.py index 49c9cd5ae..5fb137118 100644 --- a/tests/test_bezier_extraction.py +++ b/tests/test_bezier_extraction.py @@ -52,7 +52,7 @@ def test_extraction_matrices(splinetype, np_rng, request): n_matrices = spline.knot_insertion_matrix(beziers=True) beziers_n = spline.extract_bezier_patches() - for m, b in zip(n_matrices, beziers_n): + for m, b in zip(n_matrices, beziers_n, strict=True): # Test matrices m against spline ctps if "nurbs" in splinetype: assert np.allclose(b.weights, m @ spline.weights) diff --git a/tests/test_bezier_operations.py b/tests/test_bezier_operations.py index 5afc97c80..4cb53cf2e 100644 --- a/tests/test_bezier_operations.py +++ b/tests/test_bezier_operations.py @@ -98,7 +98,7 @@ def test_composition_sensitivities_on_bsplines(bspline_2p2d): composed_der_ctps = [] beziers = [] - for bez, mat in zip(extract_beziers, extraction_matrices): + for bez, mat in zip(extract_beziers, extraction_matrices, strict=True): # Composition composed, derivatives = bez.compose( inner_function, compute_sensitivities=True @@ -118,7 +118,7 @@ def test_composition_sensitivities_on_bsplines(bspline_2p2d): # Extract Beziers extract_beziers_dx = bspline_dx.extract_bezier_patches() for bez, bez_dx, comps in zip( - beziers, extract_beziers_dx, composed_der_ctps + beziers, extract_beziers_dx, composed_der_ctps, strict=True ): # Compose finite differences spline composed_dx = bez_dx.compose(inner_function) diff --git a/tests/test_knot_vectors.py b/tests/test_knot_vectors.py index 329d165b5..9d9af3457 100644 --- a/tests/test_knot_vectors.py +++ b/tests/test_knot_vectors.py @@ -12,7 +12,7 @@ def test_knot_vectors(splinetype, request): copy_knot_vectors = spline.knot_vectors[:] unique_knots = [np.unique(ckvs) for ckvs in copy_knot_vectors] - for uk, uk_fct in zip(unique_knots, spline.unique_knots): + for uk, uk_fct in zip(unique_knots, spline.unique_knots, strict=True): assert np.allclose(uk, uk_fct) # test knot_multiplicities @@ -20,7 +20,9 @@ def test_knot_vectors(splinetype, request): np.unique(ckvs, return_counts=True) for ckvs in copy_knot_vectors ] - for m_u, m_fct in zip(multiplicity, spline.knot_multiplicities): + for m_u, m_fct in zip( + multiplicity, spline.knot_multiplicities, strict=True + ): assert np.allclose(m_u[1], m_fct) # test knot_vector creation @@ -28,5 +30,6 @@ def test_knot_vectors(splinetype, request): spline.unique_knots, spline.knot_multiplicities, spline.knot_vectors, + strict=True, ): - assert np.allclose(np.array(spl_kv), np.repeat(u_kv, kn_m)) + assert np.allclose(spl_kv.numpy(), np.repeat(u_kv, kn_m)) diff --git a/tests/test_kv_manipulation.py b/tests/test_kv_manipulation.py index b3c7d63da..04516d5aa 100644 --- a/tests/test_kv_manipulation.py +++ b/tests/test_kv_manipulation.py @@ -196,7 +196,7 @@ def test_uniform_refine(): # compute what it should be n_elem2_ref = 1 - for uk, nk in zip(unique_knots1, n_knots): + for uk, nk in zip(unique_knots1, n_knots, strict=True): n_elem2_ref *= (len(uk) - 1) * (nk + 1) assert n_elem2_ref == n_elem2 diff --git a/tests/test_microstructure.py b/tests/test_microstructure.py new file mode 100644 index 000000000..2b6713adf --- /dev/null +++ b/tests/test_microstructure.py @@ -0,0 +1,210 @@ +import numpy as np +from pytest import mark, skip + +import splinepy.microstructure as ms +from splinepy.helpme.create import box +from splinepy.helpme.integrate import volume + +all_tile_classes = list(ms.tiles.everything().values()) +# Tile classes where closure should be tested +tile_classes_with_closure = [ + tile_class + for tile_class in all_tile_classes + if tile_class._closure_directions is not None +] + +# Fixed auxiliary variables specific to microstructure testing +TILING = [2, 2, 2] +BOX_DIMENSIONS = [1, 1, 1] +EPS = 1e-7 + +# TODO(#458): the following tiles fail the closure test +CLOSURE_FAILS = { + ms.tiles.InverseCross3D: "closure is special: the generated tile is the inverse " + + "geometry of the closure tile of the Cross3D-tile, hence it itself does not fill" + + " the whole unit cube.", +} +# TODO: the following tiles fail the macro-sensitivity testing +MACRO_FAILS = { + ms.tiles.Chi: "currently macro-sensitivity tests fail for the Chi-tile." +} + + +@mark.parametrize("tile_class", tile_classes_with_closure) +def test_closing_face(tile_class): + """Check if closing face is working + + Parameters + --------- + tile_class: tile class in splinepy.microstructure.tiles + Microtile + """ + + # Skip tile if it doesn't support closure + if tile_class._closure_directions is None: + skip( + f"Tile {tile_class.__name__} does not have a closure implementation. Skip" + ) + + # TODO: right now skip tiles which have faulty closures + if tile_class in CLOSURE_FAILS: + reason = CLOSURE_FAILS[tile_class] + skip( + f"Known issue: skip closure test for {tile_class.__name__}, " + f"reason: {reason}" + ) + + def check_if_closed(multipatch, closure_direction): + """Helper function to see if multipatch has a closing surface + + Parameters + ---------- + multipatch: splinepy Multipatch + Microstructure multipatch + closure_direction: str + Direction in which the closure has been applied + """ + direction_index_dict = {"x": 0, "y": 1, "z": 2} + direction_index = direction_index_dict[closure_direction] + + def min_identifier(points): + return points[:, direction_index] < EPS + + def max_identifier(points): + return ( + points[:, direction_index] + > BOX_DIMENSIONS[direction_index] - EPS + ) + + # create min- and max-boundaries of the multipatch using the identifiers + multipatch.boundary_from_function(min_identifier, boundary_id=2) + multipatch.boundary_from_function(max_identifier, boundary_id=3) + + min_patches = multipatch.boundary_multipatch(2) + max_patches = multipatch.boundary_multipatch(3) + + # Check if the closing surface has a surface area of 1 + face_min_area = sum([volume(patch) for patch in min_patches.patches]) + face_max_area = sum([volume(patch) for patch in max_patches.patches]) + + assert face_min_area > 1.0 - EPS, ( + f"The closure of the {closure_direction}_min surface is not complete. " + f"Expected area of 1, instead got {face_min_area}" + ) + assert face_max_area > 1.0 - EPS, ( + f"The closure of the {closure_direction}_max surface is not complete. " + f"Expected area of 1, instead got {face_max_area}" + ) + + tile_creator = tile_class() + generator = ms.microstructure.Microstructure( + deformation_function=box(*BOX_DIMENSIONS[: tile_creator._dim]), + microtile=tile_creator, + tiling=TILING[: tile_creator._dim], + ) + # Go through all implemented closure direction + closure_directions = { + directionname[0] for directionname in tile_creator._closure_directions + } + for closure_direction in closure_directions: + multipatch = generator.create(closing_face=closure_direction) + check_if_closed(multipatch, closure_direction) + + +@mark.parametrize("tile_class", all_tile_classes) +def test_macro_sensitivities(tile_class, np_rng, h_eps, n_test_points): + """Testing the correctness of the derivatives of the whole microstructure w.r.t. + the deformation function's control points. It is tested by evaluating the derivative + obtained via finite differences. The values are evaluated at random points. + + Parameters + ---------- + tile_class: tile class in splinepy.microstructure.tiles + Microtile + np_rng: numpy.random._generator.Generator + Default random number generator + h_eps: float + Perturbation size for finite difference evaluation. Defined in conftest.py + n_test_points: int + Number of testing points in the parametric domain. Defined in conftest.py + """ + + # TODO: right now skip tiles which cause errors + if tile_class in MACRO_FAILS: + reason = MACRO_FAILS[tile_class] + skip( + f"Known issue: skip macro-senstivity test for {tile_class.__name__}, " + f"reason: {reason}" + ) + + tile_creator = tile_class() + deformation_function_orig = box(*BOX_DIMENSIONS[: tile_creator._dim]) + generator = ms.microstructure.Microstructure( + deformation_function=deformation_function_orig, + microtile=tile_creator, + tiling=TILING[: tile_creator._dim], + ) + multipatch = generator.create(macro_sensitivities=True) + dim = multipatch.dim + n_cps = deformation_function_orig.cps.shape[0] + + # Set evaluation points as random spots in the parametric space + eval_points = np_rng.random((n_test_points, tile_creator._para_dim)) + microstructure_orig_evaluations = [ + patch.evaluate(eval_points) for patch in multipatch.patches + ] + n_patches = len(multipatch.patches) + + # Go through derivatives of every deformation function's control point + for ii_ctps in range(n_cps): + # Gradient through finite differences + deformation_function_perturbed = deformation_function_orig.copy() + deformation_function_perturbed.cps[ii_ctps, :] += h_eps + generator.deformation_function = deformation_function_perturbed + multipatch_perturbed = generator.create() + microstructure_perturbed_evaluations = [ + patch.evaluate(eval_points) + for patch in multipatch_perturbed.patches + ] + # Evaluate finite difference gradient + fd_sensitivity = [ + (patch_perturbed - patch_orig) / h_eps + for patch_perturbed, patch_orig in zip( + microstructure_perturbed_evaluations, + microstructure_orig_evaluations, + strict=True, + ) + ] + + # Go through each direction + for jj_dim in range(dim): + deriv_orig = multipatch.fields[ii_ctps * dim + jj_dim] + deriv_evaluations = [ + patch.evaluate(eval_points) for patch in deriv_orig.patches + ] + for k_patch, patch_deriv_implemented, patch_deriv_fd in zip( + range(n_patches), + deriv_evaluations, + fd_sensitivity, + strict=True, + ): + # Verify derivative shapes + assert patch_deriv_implemented.shape[1] == dim, ( + "The derivative at " + + f"patch {k_patch} has the wrong dimensions." + ) + # Assert correctness of sensitivity + assert np.allclose( + patch_deriv_implemented[:, jj_dim], + patch_deriv_fd[:, jj_dim], + ), ( + "Implemented derivative calculation for tile " + + f"{tile_class}, at patch {k_patch + 1}/{n_patches} does not " + + "match the derivative obtained using Finite Differences at " + + "the following evaluation points:\n" + + str(eval_points) + + "\nImplemented derivative:\n" + + str(patch_deriv_implemented[:, jj_dim]) + + "\nFinite difference derivative:\n" + + str(patch_deriv_fd[:, jj_dim]) + ) diff --git a/tests/test_microtiles.py b/tests/test_microtiles.py new file mode 100644 index 000000000..52d71af8b --- /dev/null +++ b/tests/test_microtiles.py @@ -0,0 +1,462 @@ +from inspect import getfullargspec + +import numpy as np +from pytest import mark, raises, skip + +import splinepy.microstructure as ms +from splinepy.utils.data import cartesian_product as _cartesian_product + +# Tolerance value for checking control points +EPS = 1e-8 + +all_tile_classes = list(ms.tiles.everything().values()) +# Tile classes where closure should be tested +tile_classes_with_closure = [ + tile_class + for tile_class in all_tile_classes + if tile_class._closure_directions is not None +] +# Tile classes where sensitivities should be tested +tile_classes_with_sensitivities = [ + tile_class + for tile_class in all_tile_classes + if tile_class._sensitivities_implemented +] + +# Skip certain tile classes for parameters testing +skip_tiles = { + ms.tiles.EllipsVoid: "control points easily lie outside unitcube", + ms.tiles.Snappy: "has no 'parameters' implemented", +} + + +def check_control_points(tile_patches): + """Helper function. Check if all of tile's control points all lie within unit + square/cube. The tolerance is defined by EPS""" + # Go through all patches + for tile_patch in tile_patches: + cps = tile_patch.control_points + valid_cp_indices = (cps >= 0.0 - EPS) & (cps <= 1.0 + EPS) + assert np.all(valid_cp_indices), ( + "Control points of tile must lie inside the unit square/cube. " + + f"Found points outside bounds: {cps[~(valid_cp_indices)]}" + ) + + +def make_bounds_feasible(bounds): + """Helper function. Bounds are understood as open set of min. and max. values. + Therefore, convert bounds to open set of these values. + + Parameters + ------------ + bounds: list> + Values of bounds + + Returns + ---------- + feasible_bounds: np.ndarray + Values of bounds + """ + feasible_bounds = [ + [min_value + EPS, max_value - EPS] for min_value, max_value in bounds + ] + return np.array(feasible_bounds) + + +@mark.parametrize("tile_class", all_tile_classes) +def test_tile_class(tile_class): + """Checks if all tile classes have the appropriate members and functions. + + Parameters + --------- + tile_class: tile class in splinepy.microstructure.tiles + Microtile + """ + # Create instance of class + tile_instance = tile_class() + tile_name = tile_class.__name__ + + required_class_variables = { + "_para_dim": int, + "_dim": int, + "_evaluation_points": np.ndarray, + "_n_info_per_eval_point": int, + "_sensitivities_implemented": bool, + "_parameter_bounds": list, + "_parameters_shape": tuple, + } + + # Get tile class' objects + members = [ + attr for attr in dir(tile_instance) if not attr.startswith("__") + ] + + # Class must have function create_tile() + assert hasattr( + tile_instance, "create_tile" + ), f"Tile class {tile_name} must have create_tile() method" + + # Tile must be able to take parameters and sensitivities as input + create_parameters = getfullargspec(tile_instance.create_tile).args + for required_param in ["parameters", "parameter_sensitivities"]: + assert required_param in create_parameters, ( + f"{tile_name}.create_tile() must have '{required_param}' as an " + "input parameter" + ) + + # Ensure closure can be handled correctly + if "closure" in create_parameters: + assert "_closure_directions" in members, ( + f"Tile class {tile_name} has closure ability. The available closure " + + "directions are missing" + ) + assert hasattr( + tile_instance, "_closing_tile" + ), f"Tile class {tile_name} has closure ability but no _closing_tile() function" + + # Check if tile class has all required variables and they are the correct type + for required_variable, var_type in required_class_variables.items(): + assert ( + required_variable in members + ), f"Tile class {tile_name} needs to have member variable '{required_variable}'" + assert isinstance( + getattr(tile_instance, required_variable), var_type + ), ( + f"Variable {required_variable} must be of type {var_type.__name__}, " + f"but found type {type(getattr(tile_instance, required_variable)).__name__}" + ) + + # Check default parameter value if there is one + if tile_instance._parameters_shape != (): + # Assert that there is a default value + assert hasattr( + tile_instance, "_default_parameter_value" + ), f'{tile_name} must have "_default_parameter_value" as a class attribute.' + # Check the default value's type + default_value = tile_instance._default_parameter_value + if isinstance(default_value, np.ndarray): + # Check the dimensions + assert default_value.shape == tile_instance._parameters_shape, ( + f"Default parameter values for tile {tile_name} has the wrong" + " dimensions" + ) + # Check if default values are within bounds + default_value = default_value.ravel() + elif not isinstance(default_value, float): + raise ValueError( + f"Default parameter value for tile {tile_name} must either be " + "a float or a numpy array" + ) + + # Check if default values are within bounds + parameter_bounds = np.asarray(tile_instance._parameter_bounds) + lower_bounds = parameter_bounds[:, 0] + upper_bounds = parameter_bounds[:, 1] + assert np.all( + (default_value > lower_bounds) & (default_value < upper_bounds) + ), f"Default parameter value of tile {tile_name} is not within bounds" + + +@mark.parametrize("tile_class", all_tile_classes) +def test_tile_bounds(tile_class): + """Test if tile is still in unit cube at the bounds. Checks default and also + non-default parameter values. + + Parameters + --------- + tile_class: tile class in splinepy.microstructure.tiles + Microtile + """ + tile_creator = tile_class() + # Create tile with default parameters + tile_patches, _ = tile_creator.create_tile() + check_control_points(tile_patches) + + # Skip certain classes for testing + if tile_class in skip_tiles: + skip( + "Known issue: skip bound test for non-default parameter values for tile " + f"{tile_class.__name__}. Reason: {skip_tiles[tile_class]}" + ) + + # Go through all extremes of parameters and ensure that also they create + # tiles within unit square/cube + feasible_parameter_bounds = make_bounds_feasible( + tile_creator._parameter_bounds + ) + all_parameter_bounds = _cartesian_product(feasible_parameter_bounds) + for parameter_extremes in all_parameter_bounds: + tile_patches, _ = tile_creator.create_tile( + parameters=parameter_extremes.reshape( + tile_creator._parameters_shape + ) + ) + check_control_points(tile_patches) + + +@mark.parametrize("tile_class", tile_classes_with_closure) +def test_tile_closure(tile_class): + """Check if closing tiles also lie in unit cube. + + Parameters + --------- + tile_class: tile class in splinepy.microstructure.tiles + Microtile + """ + tile_name = tile_class.__name__ + + # Skip tile if if does not support closure + if tile_class._closure_directions is None: + skip(f"Tile {tile_name} does not have a closure implementation. Skip") + tile_creator = tile_class() + # Go through all implemented closure directions + for closure_direction in tile_creator._closure_directions: + tile_patches, sensitivities = tile_creator.create_tile( + closure=closure_direction + ) + assert sensitivities is None, ( + f"Expected sensitivities to be None for closure {closure_direction} " + + f"when no sensitivities are requested, got {sensitivities}" + ) + + check_control_points(tile_patches) + + # Also check non-default parameters + # Skip certain classes for testing + if tile_class in skip_tiles: + skip( + "Known issue: skip closure test for non-default parameter for tile " + f"{tile_name}. Reason: {skip_tiles[tile_class]}" + ) + + # Go through all extremes of parameters and ensure that also they create + # tiles within unit square/cube + feasible_parameter_bounds = make_bounds_feasible( + tile_creator._parameter_bounds + ) + all_parameter_bounds = _cartesian_product(feasible_parameter_bounds) + # Go through all implemented closure directions + for closure_direction in tile_creator._closure_directions: + for parameter_extremes in all_parameter_bounds: + tile_patches, _ = tile_creator.create_tile( + parameters=parameter_extremes.reshape( + tile_creator._parameters_shape + ), + closure=closure_direction, + ) + check_control_points(tile_patches) + + +@mark.parametrize("tile_class", tile_classes_with_sensitivities) +def test_tile_derivatives( + tile_class, + np_rng, + h_eps, + n_test_points, + fd_derivative_stepsizes_and_weights, +): + """Testing the correctness of the tile derivatives using Finite Differences. + This includes every closure and no closure, every parameter and every patch + by evaluating at random points and for random parameters. + + Parameters + --------- + tile_class: tile class in splinepy.microstructure.tiles + Microtile + np_rng: numpy.random._generator.Generator + Default random number generator + h_eps: float + Perturbation size for finite difference evaluation. Defined in conftest.py + n_test_points: int + Number of testing points in the parametric domain. Defined in conftest.py + fd_derivative_stepsizes_and_weights: dict + Stepsizes and weights for finite difference scheme. Defined in conftest.py + """ + tile_creator = tile_class() + # Skip test if tile class has no implemented sensitivities + if not tile_creator._sensitivities_implemented: + skip(f"Tile {tile_class.__name__} has no sensitivities implemented") + + def generate_random_parameters(tile_creator, np_rng): + """Generate random parameters within bounds""" + parameter_bounds = np.array(tile_creator._parameter_bounds) + parameters = parameter_bounds[:, 0] + np_rng.random( + len(parameter_bounds) + ) * np.ptp(parameter_bounds, axis=1) + return parameters.reshape(tile_creator._parameters_shape) + + parameters = generate_random_parameters(tile_creator, np_rng) + + # Test no closure as well as ... + closure_directions = [None] + # ... every closure implemented + if tile_creator._closure_directions is not None: + closure_directions += tile_creator._closure_directions + + # Retrieve shape values of parameters + n_eval_points = tile_creator._evaluation_points.shape[0] + n_info_per_eval_point = tile_creator._n_info_per_eval_point + + def derivative_finite_difference_evaluation( + tile_creator, parameters, i_parameter, closure, eval_points, n_patches + ): + """Compute the derivative using fourth-order accurate centered finite + differences + + Parameters + ------------ + tile_creator: instance of microtile + Microtile class + parameters: np.ndarray + Tile parameter values + i_parameter: int + Index of parameter, on which the FD derivative calculation should be + performed on + closure: None/str + Closure direction of tile + eval_points: np.ndarray + Evaluation points on where to evaluate the derivative on + n_patches: int + Number of patches of tile + """ + # Initialize array for FD evaluation + fd_sensitivities = np.zeros( + (n_patches, len(eval_points), tile_creator.dim) + ) + + # Go through the + for stepsize, weighting in fd_derivative_stepsizes_and_weights.items(): + # Perturb parameter with respective stepsize + parameters_perturbed = parameters.copy() + parameters_perturbed[:, i_parameter] += stepsize * h_eps + # Create patches with perturbed parameter value + splines_perturbed, _ = tile_creator.create_tile( + parameters=parameters_perturbed, closure=closure + ) + fd_sensitivities += np.array( + [ + weighting / h_eps * spl.evaluate(eval_points) + for spl in splines_perturbed + ] + ) + + return fd_sensitivities + + # Test each closure direction + for closure in closure_directions: + # Evaluate tile with given parameter and closure configuration + splines_orig, _ = tile_creator.create_tile( + parameters=parameters, closure=closure + ) + n_patches = len(splines_orig) + # Set evaluation points as random spots in the parametric space + eval_points = np_rng.random((n_test_points, splines_orig[0].para_dim)) + # Go through all the parameters individually + for i_parameter in range(n_info_per_eval_point): + # Get implemented derivatives w.r.t. one parameter + parameter_sensitivities = np.zeros( + (n_eval_points, n_info_per_eval_point, 1) + ) + parameter_sensitivities[:, i_parameter, :] = 1 + _, derivatives = tile_creator.create_tile( + parameters=parameters, + parameter_sensitivities=parameter_sensitivities, + closure=closure, + ) + deriv_evaluations = [ + deriv.evaluate(eval_points) for deriv in derivatives[0] + ] + # Perform finite difference evaluation + fd_sensitivities = derivative_finite_difference_evaluation( + tile_creator, + parameters, + i_parameter, + closure, + eval_points, + n_patches, + ) + # Check every patch + for i_patch, deriv_orig, deriv_fd in zip( + range(n_patches), + deriv_evaluations, + fd_sensitivities, + strict=True, + ): + message = ( + f"Implemented derivative calculation for tile class {tile_class} " + f"with closure {closure}, parameter {i_parameter + 1}/" + f"{n_info_per_eval_point} at patch {i_patch + 1}/{n_patches} does" + " not match the derivative obtained using Finite Differences" + f" at the following evaluation points:\n {eval_points}\n" + "Implemented derivative:\n{deriv_orig}\n" + f"Finite Difference derivative:\n{deriv_fd}" + ) + assert np.allclose(deriv_orig, deriv_fd), message + + +@mark.parametrize("tile_class", all_tile_classes) +def test_invalid_parameter_values(tile_class, big_perturbation): + """Testing whether the tile class correctly raises an error if invalid parameters + are given. Current tests include too low or too high parameter values and wrong + shapes of the parameter array. + + Parameters + ---------- + tile_class: tile class in splinepy.microstructure.tiles + Microtile class + """ + tile_creator = tile_class() + # For certain tiles skip tests + if len(tile_creator._parameter_bounds) == 0: + skip( + f"Skip check for invalid parameters for tile {tile_class.__name__} " + "since there are no parameter bounds implemented for this tile" + ) + + parameter_bounds = np.asarray(tile_creator._parameter_bounds) + + # Check if tile class correctly raises an error if parameter values are too low + parameters_too_low = ( + parameter_bounds[:, 0].reshape(tile_creator._parameters_shape) + - big_perturbation + ) + with raises(ValueError) as exc_info_low: + tile_creator.create_tile(parameters=parameters_too_low) + # Check if the exception message calls TileBase.check_params() + assert "The following parameters are out of bounds: " in str( + exc_info_low.value + ), ( + f"Tile class {tile_class.__name__} must call TileBase.check_params() and raise", + " a ValueError if parameters values are too low", + ) + + # Check the same if parameter values are too high + parameters_too_high = ( + parameter_bounds[:, 1].reshape(tile_creator._parameters_shape) + + big_perturbation + ) + with raises(ValueError) as exc_info_high: + tile_creator.create_tile(parameters=parameters_too_high) + # Check if the exception message calls TileBase.check_params() + assert "The following parameters are out of bounds: " in str( + exc_info_high.value + ), ( + f"Tile class {tile_class.__name__} must call TileBase.check_params() and raise", + " a ValueError if parameters values are too high", + ) + + # Test if error is correctly thrown if the parameter shape is incompatible + # Take parameters in the middle of the bounds and double the array size + parameters_middle = np.mean(parameter_bounds, axis=1).reshape( + tile_creator._parameters_shape + ) + parameters_middle = np.tile(parameters_middle, [2, 1]) + # Check if the error is correctly raised + with raises(TypeError) as exc_info_size: + tile_creator.create_tile(parameters=parameters_middle) + assert "Mismatch in parameter size, expected" in str( + exc_info_size.value + ), ( + f"Tile class {tile_class.__name__} must call TileBase.check_params() and raise", + " a TypeError if the given parameters have the wrong shape", + ) diff --git a/tests/test_multipatch.py b/tests/test_multipatch.py index b2ad9cc38..51390a23a 100644 --- a/tests/test_multipatch.py +++ b/tests/test_multipatch.py @@ -207,11 +207,11 @@ def test_interfaces_and_boundaries(are_splines_equal): bmp_1 = multipatch.boundary_multipatch(1) assert len(bmp_1.patches) == 2 boundary_1 = [] - for i_patch, i_face in zip(*multipatch.boundaries[0]): + for i_patch, i_face in zip(*multipatch.boundaries[0], strict=True): boundary_1.append( *multipatch.patches[i_patch].extract.boundaries([i_face]) ) - for patch_0, patch_1 in zip(boundary_1, bmp_1.patches): + for patch_0, patch_1 in zip(boundary_1, bmp_1.patches, strict=True): assert are_splines_equal(patch_0, patch_1) assert len(multipatch.boundary_patch_ids(8)) == 0 diff --git a/tests/test_normalize_knot_vectors.py b/tests/test_normalize_knot_vectors.py index ced5ab685..494a3a6db 100644 --- a/tests/test_normalize_knot_vectors.py +++ b/tests/test_normalize_knot_vectors.py @@ -14,7 +14,9 @@ def test_bspline_normalize_knot_vectors(bspline_2p2d): # normalize bspline.normalize_knot_vectors() - for i, (ref_kv, kv) in enumerate(zip(ref, bspline.knot_vectors)): + for i, (ref_kv, kv) in enumerate( + zip(ref, bspline.knot_vectors, strict=True) + ): assert np.allclose(ref_kv, kv), f"{i}. para dim failed to normalize" @@ -31,5 +33,7 @@ def test_nurbs_normalize_knot_vectors(nurbs_2p2d): # normalize nurbs.normalize_knot_vectors() - for i, (ref_kv, kv) in enumerate(zip(ref, nurbs.knot_vectors)): + for i, (ref_kv, kv) in enumerate( + zip(ref, nurbs.knot_vectors, strict=True) + ): assert np.allclose(ref_kv, kv), f"{i}. para dim failed to normalize"