pymats 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymats-0.1.0/PKG-INFO +55 -0
- pymats-0.1.0/README.md +46 -0
- pymats-0.1.0/pymats/cae/__init__.py +42 -0
- pymats-0.1.0/pymats/cae/constitutive.py +127 -0
- pymats-0.1.0/pymats/cae/stressstraincurve.py +156 -0
- pymats-0.1.0/pymats/data/__init__.py +7 -0
- pymats-0.1.0/pymats/data/collection.py +100 -0
- pymats-0.1.0/pymats/data/loader.py +325 -0
- pymats-0.1.0/pymats/data/mechanicalproperty/__init__.py +98 -0
- pymats-0.1.0/pymats/data/mechanicalproperty/approximationmodel.py +123 -0
- pymats-0.1.0/pymats/data/mechanicalproperty/property.py +104 -0
- pymats-0.1.0/pymats/materials/__init__.py +4 -0
- pymats-0.1.0/pymats/materials/asmematerial.py +85 -0
- pymats-0.1.0/pymats/materials/constants.py +71 -0
- pymats-0.1.0/pymats/materials/custommaterials.py +31 -0
- pymats-0.1.0/pymats/materials/enmaterial.py +69 -0
- pymats-0.1.0/pymats/materials/materialbase.py +516 -0
- pymats-0.1.0/pymats/pymats.egg-info/PKG-INFO +55 -0
- pymats-0.1.0/pymats/pymats.egg-info/SOURCES.txt +39 -0
- pymats-0.1.0/pymats/pymats.egg-info/dependency_links.txt +1 -0
- pymats-0.1.0/pymats/pymats.egg-info/top_level.txt +4 -0
- pymats-0.1.0/pymats/utils/__init__.py +38 -0
- pymats-0.1.0/pymats/utils/allowables.py +122 -0
- pymats-0.1.0/pymats/utils/composition.py +71 -0
- pymats-0.1.0/pymats/utils/designpoint.py +20 -0
- pymats-0.1.0/pymats/utils/log.py +47 -0
- pymats-0.1.0/pymats/utils/mbasemodel.py +16 -0
- pymats-0.1.0/pymats/utils/myjsonencoder.py +59 -0
- pymats-0.1.0/pymats/utils/pma/builder.py +36 -0
- pymats-0.1.0/pymats/utils/pma/document.py +37 -0
- pymats-0.1.0/pymats/utils/pma/renderer.py +121 -0
- pymats-0.1.0/pymats/utils/testing.py +7 -0
- pymats-0.1.0/pymats.egg-info/PKG-INFO +40 -0
- pymats-0.1.0/pymats.egg-info/SOURCES.txt +10 -0
- pymats-0.1.0/pymats.egg-info/dependency_links.txt +1 -0
- pymats-0.1.0/pymats.egg-info/top_level.txt +1 -0
- pymats-0.1.0/pyproject.toml +19 -0
- pymats-0.1.0/setup.cfg +4 -0
- pymats-0.1.0/tests/test_config.py +0 -0
- pymats-0.1.0/tests/test_materials.py +17 -0
- pymats-0.1.0/tests/test_module.py +9 -0
pymats-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pymats
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A python module for handling materials
|
|
5
|
+
Author-email: Sebastian Mau <klixz09@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# PyMats
|
|
11
|
+
PyMats is a material database app for Python.
|
|
12
|
+
It is designed to help you manage and organize your materials and data.
|
|
13
|
+
You can create, edit, and delete materials, as well as search for materials by name or ID.
|
|
14
|
+
PyMats also allows you to copy materials from other collections and save them to your own collection.
|
|
15
|
+
The api can be used to load material data to extternal applications, like excel. In additon the pymats module
|
|
16
|
+
can be used in python directly to interact with the database.
|
|
17
|
+
|
|
18
|
+
## Main Actions
|
|
19
|
+
- Search for materials by name or ID
|
|
20
|
+
- Copy materials from other collections
|
|
21
|
+
- Save materials to your own collection
|
|
22
|
+
- Load material data to external applications
|
|
23
|
+
- Interact with the database using the pymats module
|
|
24
|
+
- Get CAE Input Files for Ansys, LS-Dyna and Abaqus
|
|
25
|
+
- Compare Materials and find equivalents
|
|
26
|
+
- Generate Particular Material Apraisals (PMAs) automatically with custom requirements
|
|
27
|
+
- Since Excel is widley used, you can create a customized excel data table with your materials
|
|
28
|
+
- Future: "Engineering Data" for Ansys Workbench
|
|
29
|
+
|
|
30
|
+
The public collections and its materials are free to use. The material data is built from regression models
|
|
31
|
+
and only **approximate** the data of the corresponding standards.
|
|
32
|
+
|
|
33
|
+
## Getting started
|
|
34
|
+
### Python Module
|
|
35
|
+
#### Installation
|
|
36
|
+
The python module is available with `pip install pymats`
|
|
37
|
+
#### Example
|
|
38
|
+
```python
|
|
39
|
+
from pymats import Material`
|
|
40
|
+
material = Material(Idenitifier="SA-723-2-2")`
|
|
41
|
+
```
|
|
42
|
+
This will load the material via the api by default. See the configuration section for defining custom databases or data sources.
|
|
43
|
+
|
|
44
|
+
## Using the API
|
|
45
|
+
The general endpoints `/load_materials`, `/material/{mid}/` and `/get_mids` can just be used like any other api endpoints. Check the [docu](/) for information of returns. If an enpoint requires authentication, provide your personal remote token as `?user=<token>`. The token can be placed in the env variable `$REMOTE_TOKEN` or your credentials are stored in `~/.pymats/credentials.json`.
|
|
46
|
+
|
|
47
|
+
## Using in Excel
|
|
48
|
+
With the token you have access to an excel addin based on OfficeJS. You can also use the token with a vba function. The addin can be downloaded via the [download section](/)
|
|
49
|
+
|
|
50
|
+
## Using in Mathcad
|
|
51
|
+
With the token you can download a compiled dll you can include to your mathcad environment to get the related udf's.
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
### Environment Variables
|
|
55
|
+
`$PYMATS_TOKEN`: used for authentication
|
pymats-0.1.0/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# PyMats
|
|
2
|
+
PyMats is a material database app for Python.
|
|
3
|
+
It is designed to help you manage and organize your materials and data.
|
|
4
|
+
You can create, edit, and delete materials, as well as search for materials by name or ID.
|
|
5
|
+
PyMats also allows you to copy materials from other collections and save them to your own collection.
|
|
6
|
+
The api can be used to load material data to extternal applications, like excel. In additon the pymats module
|
|
7
|
+
can be used in python directly to interact with the database.
|
|
8
|
+
|
|
9
|
+
## Main Actions
|
|
10
|
+
- Search for materials by name or ID
|
|
11
|
+
- Copy materials from other collections
|
|
12
|
+
- Save materials to your own collection
|
|
13
|
+
- Load material data to external applications
|
|
14
|
+
- Interact with the database using the pymats module
|
|
15
|
+
- Get CAE Input Files for Ansys, LS-Dyna and Abaqus
|
|
16
|
+
- Compare Materials and find equivalents
|
|
17
|
+
- Generate Particular Material Apraisals (PMAs) automatically with custom requirements
|
|
18
|
+
- Since Excel is widley used, you can create a customized excel data table with your materials
|
|
19
|
+
- Future: "Engineering Data" for Ansys Workbench
|
|
20
|
+
|
|
21
|
+
The public collections and its materials are free to use. The material data is built from regression models
|
|
22
|
+
and only **approximate** the data of the corresponding standards.
|
|
23
|
+
|
|
24
|
+
## Getting started
|
|
25
|
+
### Python Module
|
|
26
|
+
#### Installation
|
|
27
|
+
The python module is available with `pip install pymats`
|
|
28
|
+
#### Example
|
|
29
|
+
```python
|
|
30
|
+
from pymats import Material`
|
|
31
|
+
material = Material(Idenitifier="SA-723-2-2")`
|
|
32
|
+
```
|
|
33
|
+
This will load the material via the api by default. See the configuration section for defining custom databases or data sources.
|
|
34
|
+
|
|
35
|
+
## Using the API
|
|
36
|
+
The general endpoints `/load_materials`, `/material/{mid}/` and `/get_mids` can just be used like any other api endpoints. Check the [docu](/) for information of returns. If an enpoint requires authentication, provide your personal remote token as `?user=<token>`. The token can be placed in the env variable `$REMOTE_TOKEN` or your credentials are stored in `~/.pymats/credentials.json`.
|
|
37
|
+
|
|
38
|
+
## Using in Excel
|
|
39
|
+
With the token you have access to an excel addin based on OfficeJS. You can also use the token with a vba function. The addin can be downloaded via the [download section](/)
|
|
40
|
+
|
|
41
|
+
## Using in Mathcad
|
|
42
|
+
With the token you can download a compiled dll you can include to your mathcad environment to get the related udf's.
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
### Environment Variables
|
|
46
|
+
`$PYMATS_TOKEN`: used for authentication
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from typing import Optional, Literal
|
|
3
|
+
from pymats import MaterialBase
|
|
4
|
+
from pymats.cae.constitutive import ConstitutiveModel, IsotropicModel
|
|
5
|
+
from pymats.cae.stressstraincurve import StressStrainCurve, ModelConstants
|
|
6
|
+
|
|
7
|
+
class CAEModel:
|
|
8
|
+
|
|
9
|
+
material: MaterialBase
|
|
10
|
+
|
|
11
|
+
def __init__(self, material: MaterialBase) -> None:
|
|
12
|
+
self.material = material
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_stress_strain_curve(self, approach: ModelConstants.Approaches) -> StressStrainCurve:
|
|
16
|
+
a_s = self.material.ElongationBreak.long if self.material.ElongationBreak else 0.12
|
|
17
|
+
if np.isclose(a_s, 0.0):
|
|
18
|
+
a_s = 1.2
|
|
19
|
+
curve = StressStrainCurve(
|
|
20
|
+
Model=approach,
|
|
21
|
+
youngs_modulus=self.material.EO,
|
|
22
|
+
yield_strength=self.material.SYo,
|
|
23
|
+
tensile_strength=self.material.STo,
|
|
24
|
+
elongation_at_break=a_s
|
|
25
|
+
)
|
|
26
|
+
curve.attach_material(self.material)
|
|
27
|
+
curve.build()
|
|
28
|
+
return curve
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_constitutive_model(
|
|
32
|
+
self, d: Optional[np.ndarray] = None,
|
|
33
|
+
kinematics: Literal['full', 'axissymmetric', 'plane strain', 'plane stress'] = 'full'
|
|
34
|
+
) -> ConstitutiveModel:
|
|
35
|
+
return IsotropicModel(
|
|
36
|
+
d or IsotropicModel.compute_d(
|
|
37
|
+
self.material.EO,
|
|
38
|
+
self.material.PoissonsRatio,
|
|
39
|
+
kinematics
|
|
40
|
+
),
|
|
41
|
+
self.material.EO, self.material.PoissonsRatio
|
|
42
|
+
)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
import numpy as np
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Plasticity:
|
|
7
|
+
|
|
8
|
+
tangent_modulus: float = 203e3
|
|
9
|
+
@property
|
|
10
|
+
def H(self):
|
|
11
|
+
return self.tangent_modulus
|
|
12
|
+
|
|
13
|
+
def __init__(self, e: float) -> None:
|
|
14
|
+
# initalized with tangent modulus as youngs modulus for no plasticity
|
|
15
|
+
self.tangent_modulus = e
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ConstitutiveModel(ABC):
|
|
20
|
+
|
|
21
|
+
plasticity: Plasticity
|
|
22
|
+
|
|
23
|
+
_d: np.ndarray
|
|
24
|
+
@property
|
|
25
|
+
def D(self):
|
|
26
|
+
return self._d
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def compute_d(cls, *args, **kwargs) -> np.ndarray:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
def __init__(self, d: np.ndarray, e: float, nu: float) -> None:
|
|
34
|
+
super().__init__()
|
|
35
|
+
self._d = d
|
|
36
|
+
self.plasticity = Plasticity(e)
|
|
37
|
+
|
|
38
|
+
def compute_elastic_stress(self, eps: np.ndarray):
|
|
39
|
+
return self._d @ eps
|
|
40
|
+
|
|
41
|
+
def set_plastiticity(
|
|
42
|
+
self,
|
|
43
|
+
plasticity: Literal['linear hardening' ,'perfect'],
|
|
44
|
+
sy: float,
|
|
45
|
+
st: float,
|
|
46
|
+
e: float,
|
|
47
|
+
a_s: float
|
|
48
|
+
):
|
|
49
|
+
if plasticity == 'perfect':
|
|
50
|
+
self.plasticity.tangent_modulus = 0.0
|
|
51
|
+
elif plasticity == 'linear hardening':
|
|
52
|
+
eps_el = sy / e
|
|
53
|
+
eps_pl = a_s - eps_el
|
|
54
|
+
ds = st - sy
|
|
55
|
+
self.plasticity.tangent_modulus = ds / eps_pl
|
|
56
|
+
|
|
57
|
+
class IsotropicModel(ConstitutiveModel):
|
|
58
|
+
|
|
59
|
+
# lame parameters
|
|
60
|
+
mu: float
|
|
61
|
+
lam: float
|
|
62
|
+
# ##################
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def compute_lame_parameters(cls, e, nu):
|
|
67
|
+
mu = e / (2 * (1 + nu))
|
|
68
|
+
lam = e * nu / ((1 + nu) * (1 - 2 * nu))
|
|
69
|
+
return mu, lam
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def compute_axissymmetric_stiffness(cls, e: float, nu: float):
|
|
73
|
+
c = e / ((1 + nu) * (1 - 2 * nu))
|
|
74
|
+
return c * np.array([
|
|
75
|
+
[1 - nu, nu, nu, 0],
|
|
76
|
+
[nu, 1 - nu, nu, 0],
|
|
77
|
+
[nu, nu, 1 - nu, 0],
|
|
78
|
+
[0, 0, 0, (1 - 2 * nu) / 2]
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def compute_plane_strain_stiffness(cls, e: float, nu: float):
|
|
83
|
+
c = e / ((1 + nu) * (1 - 2 * nu))
|
|
84
|
+
return c * np.array([
|
|
85
|
+
[1 - nu, nu, 0],
|
|
86
|
+
[nu, 1 - nu, 0],
|
|
87
|
+
[0, 0, (1 - 2 * nu) / 2.0]
|
|
88
|
+
])
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def compute_plane_stress_stiffness(cls, e: float, nu: float):
|
|
92
|
+
c = e / (1 - nu **2)
|
|
93
|
+
return c * np.array([
|
|
94
|
+
[1, nu, 0],
|
|
95
|
+
[nu, 1, 0],
|
|
96
|
+
[0, 0, (1 - nu) / 2]
|
|
97
|
+
])
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def compute_full_stiffness(cls, e: float, nu: float):
|
|
101
|
+
mu, lam = cls.compute_lame_parameters(e, nu)
|
|
102
|
+
d = np.zeros((6, 6))
|
|
103
|
+
d[:3, :3] = lam
|
|
104
|
+
np.fill_diagonal(d[:3, :3], lam + 2 * mu)
|
|
105
|
+
d[3:, 3:] = np.eye(3) * mu
|
|
106
|
+
return d
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def compute_d(cls, e: float, nu: float, kinematics: Literal['full', 'plane strain', 'plane stress', 'axissymmetric']) -> np.ndarray:
|
|
110
|
+
|
|
111
|
+
if kinematics == 'axissymmetric':
|
|
112
|
+
d = cls.compute_axissymmetric_stiffness(e, nu)
|
|
113
|
+
elif kinematics == 'plane strain':
|
|
114
|
+
d = cls.compute_plane_strain_stiffness(e, nu)
|
|
115
|
+
elif kinematics == 'plane stress':
|
|
116
|
+
d = cls.compute_plane_stress_stiffness(e, nu)
|
|
117
|
+
elif kinematics == 'full':
|
|
118
|
+
d = cls.compute_full_stiffness(e, nu)
|
|
119
|
+
else:
|
|
120
|
+
raise ValueError("Don't understand kinematic requirement!")
|
|
121
|
+
return d
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def __init__(self, d: np.ndarray, e: float, nu: float) -> None:
|
|
125
|
+
super().__init__(d, e, nu)
|
|
126
|
+
self.mu, self.lam = self.compute_lame_parameters(e, nu)
|
|
127
|
+
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from pydantic import computed_field, PrivateAttr
|
|
5
|
+
|
|
6
|
+
from pymats.utils.log import logger
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from pymats.utils.mbasemodel import MBaseModel
|
|
9
|
+
from pymats import MaterialBase
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ModelConstants:
|
|
13
|
+
class Approaches(str, Enum):
|
|
14
|
+
LINEARELASTIC = "linearelastic"
|
|
15
|
+
PERFECTPLASTICITY = "perfectplastic"
|
|
16
|
+
LINEARHARDENING = "linearhardening"
|
|
17
|
+
|
|
18
|
+
class StressStrainCurve(MBaseModel):
|
|
19
|
+
|
|
20
|
+
_data: np.ndarray = PrivateAttr(default=np.array((2, 2)))
|
|
21
|
+
_material: MaterialBase | None = PrivateAttr(default=None)
|
|
22
|
+
@computed_field(return_type=np.ndarray)
|
|
23
|
+
@property
|
|
24
|
+
def Data(self):
|
|
25
|
+
return self._data
|
|
26
|
+
Model: ModelConstants.Approaches
|
|
27
|
+
|
|
28
|
+
# material properties
|
|
29
|
+
youngs_modulus: float
|
|
30
|
+
yield_strength: float
|
|
31
|
+
tensile_strength: float
|
|
32
|
+
elongation_at_break: float
|
|
33
|
+
|
|
34
|
+
def attach_material(self, material: MaterialBase) -> None:
|
|
35
|
+
self._material = material
|
|
36
|
+
|
|
37
|
+
def get_elastic_strain(self) -> float:
|
|
38
|
+
if not self._material:
|
|
39
|
+
raise ValueError("Material not attached to stress-strain curve.")
|
|
40
|
+
if not np.isclose(self._material.EO, 0.0):
|
|
41
|
+
return self._material.SYo / self._material.EO
|
|
42
|
+
raise ZeroDivisionError("The Young's modulus is zero! Check material data.")
|
|
43
|
+
|
|
44
|
+
def get_ansys_model(self) -> str:
|
|
45
|
+
if not self._material:
|
|
46
|
+
raise ValueError("Material not attached to stress-strain curve.")
|
|
47
|
+
lines = [
|
|
48
|
+
"/PREP7",
|
|
49
|
+
f"! Material {self._material.Synopsis} properties",
|
|
50
|
+
f"MP, EX, {self._material.MID}, {self._material.EO}",
|
|
51
|
+
f"MP, PRXY, {self._material.MID}, {self._material.PoissonsRatio}",
|
|
52
|
+
f"MP, DENS, {self._material.MID}, {self._material.Density}",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
if self.Model in [ModelConstants.Approaches.LINEARHARDENING, ModelConstants.Approaches.PERFECTPLASTICITY]:
|
|
56
|
+
hardening_modulus = 0.0
|
|
57
|
+
if self.Model == ModelConstants.Approaches.LINEARHARDENING:
|
|
58
|
+
a_s = self._material.ElongationBreak.long if self._material.ElongationBreak else 0.12
|
|
59
|
+
eps_el = self.get_elastic_strain()
|
|
60
|
+
hardening_modulus = (self._material.STo - self._material.SYo) / (a_s - eps_el)
|
|
61
|
+
|
|
62
|
+
lines.extend([
|
|
63
|
+
"! Plasticity with isotropic hardening",
|
|
64
|
+
f"TB, PLASTIC, {self._material.MID}, 1, 1",
|
|
65
|
+
f"TBPT, 1, {self._material.SYo}, {hardening_modulus}"
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
lines.append("/FINISH")
|
|
69
|
+
return "\n".join(lines)
|
|
70
|
+
|
|
71
|
+
def get_dyna_model(self) -> str:
|
|
72
|
+
if not self._material:
|
|
73
|
+
raise ValueError("Material not attached to stress-strain curve.")
|
|
74
|
+
lines = ["*KEYWORD"]
|
|
75
|
+
args = ["MID", "RO", "E", "PR"]
|
|
76
|
+
values = [self._material.MID, self._material.Density, self._material.EO, self._material.PoissonsRatio]
|
|
77
|
+
|
|
78
|
+
if self.Model == ModelConstants.Approaches.LINEARELASTIC:
|
|
79
|
+
lines.append("*MAT_ELASTIC")
|
|
80
|
+
else:
|
|
81
|
+
lines.append("*MAT_PIECEWISE_LINEAR_PLASTICITY")
|
|
82
|
+
args.extend(["SIGY", "ETAN", "FAIL", "TDEL"])
|
|
83
|
+
values.extend([self._material.SYo, 0.0, 0.0, 0.0])
|
|
84
|
+
|
|
85
|
+
lines.append("$ " + "\t".join(args))
|
|
86
|
+
lines.append("\t".join(map(str, values)))
|
|
87
|
+
|
|
88
|
+
if self.Model != ModelConstants.Approaches.LINEARELASTIC:
|
|
89
|
+
hardening_stress = self._material.STo if self.Model == ModelConstants.Approaches.LINEARHARDENING else self._material.SYo
|
|
90
|
+
lines.extend([
|
|
91
|
+
"$ Epsilon-Plastic Strain vs. Stress Data",
|
|
92
|
+
"$ EPS\tSIG",
|
|
93
|
+
f"0.0\t{self._material.SYo}",
|
|
94
|
+
f"{self._material.ElongationBreak}\t{hardening_stress}"
|
|
95
|
+
])
|
|
96
|
+
|
|
97
|
+
lines.append("*END")
|
|
98
|
+
return "\n".join(lines)
|
|
99
|
+
|
|
100
|
+
def get_abaqus_model(self) -> str:
|
|
101
|
+
if not self._material:
|
|
102
|
+
raise ValueError("Material not attached to stress-strain curve.")
|
|
103
|
+
lines = [
|
|
104
|
+
f"*Material, NAME={self._material.Synopsis}",
|
|
105
|
+
"*DENSITY",
|
|
106
|
+
f"{self._material.Density}",
|
|
107
|
+
"*ELASTIC",
|
|
108
|
+
f"{self._material.EO}, {self._material.PoissonsRatio}"
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
if self.Model != ModelConstants.Approaches.LINEARELASTIC:
|
|
112
|
+
lines.extend([
|
|
113
|
+
"*PLASTIC",
|
|
114
|
+
f"{self._material.SYo}, 0.0",
|
|
115
|
+
f"{self._material.STo}, {self._material.ElongationBreak}"
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
return "\n".join(lines)
|
|
119
|
+
|
|
120
|
+
def _build_linear_elastic(self):
|
|
121
|
+
self._data = np.array([
|
|
122
|
+
[0, 0],
|
|
123
|
+
[self.yield_strength / self.youngs_modulus, self.yield_strength]
|
|
124
|
+
])
|
|
125
|
+
|
|
126
|
+
def _build_perfect_plasticity(self):
|
|
127
|
+
self._data = np.array([
|
|
128
|
+
[0, 0],
|
|
129
|
+
[self.yield_strength / self.youngs_modulus, self.yield_strength],
|
|
130
|
+
[0.3, self.yield_strength]
|
|
131
|
+
])
|
|
132
|
+
|
|
133
|
+
def _build_linear_plasticity(self):
|
|
134
|
+
self._data = np.array([
|
|
135
|
+
[0, 0],
|
|
136
|
+
[self.yield_strength / self.youngs_modulus, self.yield_strength],
|
|
137
|
+
[self.elongation_at_break, self.tensile_strength]
|
|
138
|
+
])
|
|
139
|
+
|
|
140
|
+
def add_material_properties(self, *args):
|
|
141
|
+
self.youngs_modulus, self.yield_strength, self.tensile_strength, self.elongation_at_break = args
|
|
142
|
+
|
|
143
|
+
def reset(self):
|
|
144
|
+
self._data = np.zeros((2, 2))
|
|
145
|
+
|
|
146
|
+
def build(self):
|
|
147
|
+
try:
|
|
148
|
+
if self.Model == ModelConstants.Approaches.LINEARELASTIC:
|
|
149
|
+
self._build_linear_elastic()
|
|
150
|
+
elif self.Model == ModelConstants.Approaches.PERFECTPLASTICITY:
|
|
151
|
+
self._build_perfect_plasticity()
|
|
152
|
+
elif self.Model == ModelConstants.Approaches.LINEARHARDENING:
|
|
153
|
+
self._build_linear_plasticity()
|
|
154
|
+
except ZeroDivisionError:
|
|
155
|
+
logger.warning("Division by zero error!")
|
|
156
|
+
return
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from typing import List, Union, Set, Type
|
|
4
|
+
from sklearn.metrics.pairwise import cosine_similarity
|
|
5
|
+
|
|
6
|
+
from pydantic import PrivateAttr, computed_field, SerializeAsAny
|
|
7
|
+
|
|
8
|
+
from pymats.materials.asmematerial import ASMEMaterial
|
|
9
|
+
from pymats.materials.enmaterial import ENMaterial
|
|
10
|
+
from pymats.materials.materialbase import MaterialBase
|
|
11
|
+
from pymats.materials.constants import MaterialDataConstants
|
|
12
|
+
from pymats.data.loader import MaterialLoaderBase
|
|
13
|
+
from pymats import Material, MaterialFactory
|
|
14
|
+
from pymats.utils.log import logger
|
|
15
|
+
from pymats.config import settings
|
|
16
|
+
from pymats.utils.mbasemodel import MBaseModel
|
|
17
|
+
|
|
18
|
+
class MaterialCollection(MBaseModel):
|
|
19
|
+
|
|
20
|
+
CollectionName: str
|
|
21
|
+
IsCustomCollection: bool = False
|
|
22
|
+
materials: list[SerializeAsAny[MaterialBase]] = []
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def load_from_db(
|
|
26
|
+
cls,
|
|
27
|
+
handler: MaterialLoaderBase,
|
|
28
|
+
collection_name: str,
|
|
29
|
+
identifiers: list[str] = [],
|
|
30
|
+
include_property_data: bool = True
|
|
31
|
+
) -> "MaterialCollection":
|
|
32
|
+
|
|
33
|
+
is_custom = collection_name in settings.CustomCollectionNames
|
|
34
|
+
|
|
35
|
+
docs = handler.load_collection_materials(
|
|
36
|
+
collection_name,
|
|
37
|
+
material_names=identifiers,
|
|
38
|
+
include_property_data=include_property_data
|
|
39
|
+
)
|
|
40
|
+
materials = []
|
|
41
|
+
for doc in docs.values():
|
|
42
|
+
try:
|
|
43
|
+
materials.append(
|
|
44
|
+
MaterialFactory.from_data(doc, init_mech_properties=False)
|
|
45
|
+
)
|
|
46
|
+
except Exception as ex:
|
|
47
|
+
name = doc.get('Name', 'none')
|
|
48
|
+
logger.warning(f'Failed to load material {str(name)} for collection {str(collection_name)} with {str(ex)}')
|
|
49
|
+
continue
|
|
50
|
+
return cls(CollectionName=collection_name, IsCustomCollection=is_custom, materials=materials)
|
|
51
|
+
|
|
52
|
+
@computed_field(return_type=list[str])
|
|
53
|
+
@property
|
|
54
|
+
def Keys(self):
|
|
55
|
+
if len(self.materials) > 0:
|
|
56
|
+
class_t = self.materials[0].__class__
|
|
57
|
+
else:
|
|
58
|
+
class_t = Material
|
|
59
|
+
return list(class_t.model_fields.keys())
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def is_standard_collection(self) -> bool:
|
|
63
|
+
"""
|
|
64
|
+
Check if the collection is a standard collection (ASME, EN).
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
bool: True if the collection is a standard collection, False otherwise.
|
|
68
|
+
"""
|
|
69
|
+
return self.CollectionName in MaterialDataConstants.Collections.__members__.values()
|
|
70
|
+
|
|
71
|
+
def find_equivalents(self, material: MaterialBase, tolerance: float = 0.0):
|
|
72
|
+
master = material.tokenize(20 ,0)
|
|
73
|
+
for mat in self.materials:
|
|
74
|
+
try:
|
|
75
|
+
if mat.Identifier == material.Identifier:
|
|
76
|
+
continue
|
|
77
|
+
candidate = mat.tokenize(20, 0)
|
|
78
|
+
sim = cosine_similarity(master.reshape(1, -1), candidate.reshape(1, -1))
|
|
79
|
+
# euclidean = np.linalg.norm(master.reshape(1, -1) - candidate.reshape(1, -1))
|
|
80
|
+
if np.max(sim) >= tolerance:
|
|
81
|
+
yield (mat, np.max(sim))
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.error(f"Failed to compare materials: {e}")
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
def get_equivalents(self, material: MaterialBase, tolerance: float = 0.0):
|
|
87
|
+
equivalents = []
|
|
88
|
+
for mat in self.find_equivalents(material, tolerance):
|
|
89
|
+
material, sim = mat
|
|
90
|
+
equivalents.append({
|
|
91
|
+
"material": material.Synopsis,
|
|
92
|
+
"standard": material.Standards,
|
|
93
|
+
"score": sim
|
|
94
|
+
})
|
|
95
|
+
df = pd.DataFrame(equivalents)
|
|
96
|
+
df.sort_values(by="score", ascending=False, inplace=True)
|
|
97
|
+
df.reset_index(drop=True, inplace=True)
|
|
98
|
+
df = df.iloc[:10, :]
|
|
99
|
+
return df.to_dict(orient="records")
|
|
100
|
+
|