pymats 0.1.0__py3-none-any.whl
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.
- cae/__init__.py +42 -0
- cae/constitutive.py +127 -0
- cae/stressstraincurve.py +156 -0
- data/__init__.py +7 -0
- data/collection.py +100 -0
- data/loader.py +325 -0
- data/mechanicalproperty/__init__.py +98 -0
- data/mechanicalproperty/approximationmodel.py +123 -0
- data/mechanicalproperty/property.py +104 -0
- materials/__init__.py +4 -0
- materials/asmematerial.py +85 -0
- materials/constants.py +71 -0
- materials/custommaterials.py +31 -0
- materials/enmaterial.py +69 -0
- materials/materialbase.py +516 -0
- pymats-0.1.0.dist-info/METADATA +55 -0
- pymats-0.1.0.dist-info/RECORD +30 -0
- pymats-0.1.0.dist-info/WHEEL +5 -0
- pymats-0.1.0.dist-info/top_level.txt +4 -0
- utils/__init__.py +38 -0
- utils/allowables.py +122 -0
- utils/composition.py +71 -0
- utils/designpoint.py +20 -0
- utils/log.py +47 -0
- utils/mbasemodel.py +16 -0
- utils/myjsonencoder.py +59 -0
- utils/pma/builder.py +36 -0
- utils/pma/document.py +37 -0
- utils/pma/renderer.py +121 -0
- utils/testing.py +7 -0
cae/__init__.py
ADDED
|
@@ -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
|
+
)
|
cae/constitutive.py
ADDED
|
@@ -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
|
+
|
cae/stressstraincurve.py
ADDED
|
@@ -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
|
data/__init__.py
ADDED
data/collection.py
ADDED
|
@@ -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
|
+
|