granular 0.0.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.
- granular-0.0.0/LICENSE +21 -0
- granular-0.0.0/PKG-INFO +27 -0
- granular-0.0.0/README.md +2 -0
- granular-0.0.0/granular/__init__.py +0 -0
- granular-0.0.0/granular/geometry.py +202 -0
- granular-0.0.0/granular/grain.py +161 -0
- granular-0.0.0/granular.egg-info/PKG-INFO +27 -0
- granular-0.0.0/granular.egg-info/SOURCES.txt +11 -0
- granular-0.0.0/granular.egg-info/dependency_links.txt +1 -0
- granular-0.0.0/granular.egg-info/requires.txt +2 -0
- granular-0.0.0/granular.egg-info/top_level.txt +1 -0
- granular-0.0.0/setup.cfg +4 -0
- granular-0.0.0/setup.py +45 -0
granular-0.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Steffen Richters-Finger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
granular-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: granular
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: python library for the numerical simulation of granular materials
|
|
5
|
+
Home-page: https://pypi.org/project/granular/
|
|
6
|
+
Author: Steffen Richters-Finger
|
|
7
|
+
Author-email: srichters@uni-muenster.de
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Source, https://github.com/RichtersFinger/granular
|
|
10
|
+
Platform: UNKNOWN
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
|
|
24
|
+
# granular
|
|
25
|
+
library for the numerical simulation of granular materials
|
|
26
|
+
|
|
27
|
+
|
granular-0.0.0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# geometry.py
|
|
3
|
+
|
|
4
|
+
This module contains definitions that can be used to generate and process
|
|
5
|
+
grain shapes that are supported by `granular`, i.e., star domains.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Sampling
|
|
9
|
+
A sampling-process (useful for generating different representations of
|
|
10
|
+
grains) can be performed using the `SamplingStrategy`-classes. These,
|
|
11
|
+
again, require a `SamplingContext` storing information like number of
|
|
12
|
+
samples.
|
|
13
|
+
|
|
14
|
+
Generate an array of equidistantly distributed samples for the
|
|
15
|
+
function x**2 by entering
|
|
16
|
+
```
|
|
17
|
+
>>> from granular.geometry import EquidistantSampling, SamplingContext
|
|
18
|
+
>>> samples = EquidistantSampling.sample(shape=lambda x: x**2, context=SamplingContext())
|
|
19
|
+
>>> np.shape(samples)
|
|
20
|
+
(50, 2)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Shape generating functions
|
|
24
|
+
This module provides functions that can be used to generate `Shape`s,
|
|
25
|
+
i.e., functions which themselves represent closed, planar curves. For
|
|
26
|
+
example, using the factory `shape_superformula`, a star-like `Shape` can
|
|
27
|
+
be created by
|
|
28
|
+
```
|
|
29
|
+
>>> from granular.geometry import shape_superformula
|
|
30
|
+
>>> shape = shape_superformula((2, 9, 9), 5, 1.0)
|
|
31
|
+
>>> shape([1, 2, 3])
|
|
32
|
+
array([0.37628812, 0.77859841, 0.7103601])
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from typing import TypeAlias, Callable, Optional
|
|
37
|
+
from abc import ABC, abstractmethod
|
|
38
|
+
from dataclasses import dataclass, field
|
|
39
|
+
from math import cos, sin
|
|
40
|
+
import numpy as np
|
|
41
|
+
from numpy.typing import NDArray
|
|
42
|
+
from scipy import optimize
|
|
43
|
+
|
|
44
|
+
Shape: TypeAlias = Callable[[float | NDArray], float | NDArray]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class SamplingContext:
|
|
49
|
+
"""
|
|
50
|
+
Record class representing information on a sampling context.
|
|
51
|
+
|
|
52
|
+
Keyword arguments:
|
|
53
|
+
domain -- domain over which the sampling should be done
|
|
54
|
+
(default corresponds to an interval [0:1])
|
|
55
|
+
num -- total number of samples
|
|
56
|
+
(default 50)
|
|
57
|
+
endpoint -- if `True`, the first and last sample are identical
|
|
58
|
+
(default `False`)
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
domain: list = field(default_factory=lambda: [0.0, 1.0])
|
|
62
|
+
num: int = 50
|
|
63
|
+
endpoint: bool = False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class SamplingStrategy(ABC):
|
|
67
|
+
"""
|
|
68
|
+
Interface for sampling strategies.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def samples(
|
|
74
|
+
shape: Shape,
|
|
75
|
+
context: Optional[SamplingContext] = None
|
|
76
|
+
) -> NDArray:
|
|
77
|
+
"""
|
|
78
|
+
Returns an array of `num` sample locations based on `shape`.
|
|
79
|
+
|
|
80
|
+
Keyword arguments:
|
|
81
|
+
shape -- callable function generating a shape to be sampled
|
|
82
|
+
context -- sampling context
|
|
83
|
+
(default uses a `SamplingContext` with default
|
|
84
|
+
values)
|
|
85
|
+
"""
|
|
86
|
+
raise NotImplementedError
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def sample(
|
|
90
|
+
cls,
|
|
91
|
+
shape: Shape,
|
|
92
|
+
context: Optional[SamplingContext] = None,
|
|
93
|
+
values_only: bool = False
|
|
94
|
+
) -> NDArray:
|
|
95
|
+
"""
|
|
96
|
+
Returns an array of `num` sampled locations based on `shape`
|
|
97
|
+
as pairs of sample position and sampled value (shape=(num, 2)).
|
|
98
|
+
|
|
99
|
+
Keyword arguments:
|
|
100
|
+
shape -- callable function generating a shape to be sampled
|
|
101
|
+
context -- sampling context
|
|
102
|
+
(default uses a `SamplingContext` with default
|
|
103
|
+
values)
|
|
104
|
+
values_only -- if `True`, the returned array only contains the
|
|
105
|
+
sampled values (shape=(num,))
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
x = cls.samples(shape, context=context)
|
|
109
|
+
y = shape(x)
|
|
110
|
+
|
|
111
|
+
if values_only:
|
|
112
|
+
return y
|
|
113
|
+
return np.stack((x, y), axis=-1)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class EquidistantSampling(SamplingStrategy):
|
|
117
|
+
"""
|
|
118
|
+
Samples for equidistant sampling.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
def samples(
|
|
123
|
+
shape: Shape,
|
|
124
|
+
context: Optional[SamplingContext] = None
|
|
125
|
+
) -> NDArray:
|
|
126
|
+
return np.linspace(
|
|
127
|
+
*(context.domain or [0.0, 1.0]),
|
|
128
|
+
num=context.num,
|
|
129
|
+
endpoint=context.endpoint
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class FavorCurvatureSampling(SamplingStrategy):
|
|
134
|
+
"""
|
|
135
|
+
Samples with higher frequency in regions of greater curvature.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def samples(
|
|
140
|
+
shape: Shape,
|
|
141
|
+
context: Optional[SamplingContext] = None
|
|
142
|
+
) -> NDArray:
|
|
143
|
+
|
|
144
|
+
raise NotImplementedError
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def shape_superformula(
|
|
148
|
+
n: tuple[int, int, int],
|
|
149
|
+
m: int, r: float,
|
|
150
|
+
vectorize: bool = True
|
|
151
|
+
) -> Shape:
|
|
152
|
+
"""
|
|
153
|
+
Returns a callable function that generates a planar and closed
|
|
154
|
+
curve based on the superformula.
|
|
155
|
+
|
|
156
|
+
r(p) ~ r * [|cos(mp/4)|**n2 + |sin(mp/4)|**n3]**(-1/n1)
|
|
157
|
+
|
|
158
|
+
Keyword arguments:
|
|
159
|
+
n -- three-tuple containing the interger values `n1`, `n2`, `n3`
|
|
160
|
+
m -- angular frequency `m`
|
|
161
|
+
r -- radius of the bounding sphere
|
|
162
|
+
vectorize -- if `True`, use `np.vectorize` to make compatible with
|
|
163
|
+
`NDArray`
|
|
164
|
+
(default `True`)
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def _generate_super(_r):
|
|
168
|
+
def __r(p):
|
|
169
|
+
return _r * (
|
|
170
|
+
(abs(cos((_p := 0.25*m*p))))**n[1]
|
|
171
|
+
+ (abs(sin(_p)))**n[2]
|
|
172
|
+
)**(-1.0/n[0])
|
|
173
|
+
if vectorize:
|
|
174
|
+
return np.vectorize(__r)
|
|
175
|
+
return __r
|
|
176
|
+
|
|
177
|
+
# rescale to precisely fit bounding volume into requested size r
|
|
178
|
+
return _generate_super(
|
|
179
|
+
r/_generate_super(r)(
|
|
180
|
+
optimize.fminbound(_generate_super(-r), 0.0, 2.0*np.pi)
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def shape_fourier(
|
|
186
|
+
d: tuple[float, ...],
|
|
187
|
+
r: float,
|
|
188
|
+
vectorize: bool = True
|
|
189
|
+
) -> Shape:
|
|
190
|
+
"""
|
|
191
|
+
Returns a callable function that generates an irregular, planar and
|
|
192
|
+
closed curve based on the a Fourier expansion.
|
|
193
|
+
|
|
194
|
+
Keyword arguments:
|
|
195
|
+
d -- tuple of Fourier descriptors
|
|
196
|
+
r -- radius of the bounding sphere
|
|
197
|
+
vectorize -- if `True`, use `np.vectorize` to make compatible with
|
|
198
|
+
`NDArray`
|
|
199
|
+
(default `True`)
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# grain.py
|
|
3
|
+
|
|
4
|
+
This module defines the `Grain`-base class as well as more specialized
|
|
5
|
+
classes and supplemental helper-functions for an efficient generation of
|
|
6
|
+
`Grain`s.
|
|
7
|
+
|
|
8
|
+
## Grain
|
|
9
|
+
A `Grain` stores information on shape (including preprocessed
|
|
10
|
+
information that is being used in the simulation afterwards). It also
|
|
11
|
+
gets assigned a unique identifier via the uuid-module (uuid4).
|
|
12
|
+
Furthermore, `Grain`-type classes provide a `sample`-method that can be
|
|
13
|
+
issued to generate a sampled representation in the form of a `numpy`-
|
|
14
|
+
`NDArray`.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Optional
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
import uuid
|
|
21
|
+
import numpy as np
|
|
22
|
+
from numpy.typing import NDArray
|
|
23
|
+
from scipy import optimize
|
|
24
|
+
from granular.geometry \
|
|
25
|
+
import Shape, SamplingStrategy, EquidistantSampling, SamplingContext
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class Representations:
|
|
30
|
+
"""
|
|
31
|
+
Record class defining the storage-format of different
|
|
32
|
+
representations of `Grain`s.
|
|
33
|
+
|
|
34
|
+
Keyword arguments:
|
|
35
|
+
ground_truth -- ground truth for the shape of a `Grain`
|
|
36
|
+
sampled -- list of `NDArray`s for the sampled `Shape` at different
|
|
37
|
+
levels of detail; `sampled[0]` represents the lowest lod
|
|
38
|
+
(default `None`)
|
|
39
|
+
bounding_box -- (UNUSED) bounding box representation
|
|
40
|
+
(default `None`)
|
|
41
|
+
bounding_sphere -- `float` characterizing size of the bounding
|
|
42
|
+
sphere with a point of reference located at the
|
|
43
|
+
reference point for the shape-generating function
|
|
44
|
+
(default `None`)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
ground_truth: Shape
|
|
48
|
+
sampled: Optional[list[NDArray]] = None
|
|
49
|
+
bounding_box: Optional = None
|
|
50
|
+
bounding_sphere: Optional[float] = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Grain:
|
|
54
|
+
"""
|
|
55
|
+
`Grain`s represent the granular units of a simulation.
|
|
56
|
+
|
|
57
|
+
Keyword arguments:
|
|
58
|
+
shape -- grain shape
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, shape: Shape) -> None:
|
|
62
|
+
self._identifier = uuid.uuid4()
|
|
63
|
+
self._default_sampling_strategy = EquidistantSampling
|
|
64
|
+
self._default_sampling_context = SamplingContext(
|
|
65
|
+
domain=[0, np.pi],
|
|
66
|
+
num=50,
|
|
67
|
+
endpoint=False
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# preprocess/analyze shape
|
|
71
|
+
self._representations = Representations(
|
|
72
|
+
ground_truth=shape,
|
|
73
|
+
sampled=[
|
|
74
|
+
self._default_sampling_strategy.sample(
|
|
75
|
+
shape,
|
|
76
|
+
self._default_sampling_context
|
|
77
|
+
)
|
|
78
|
+
],
|
|
79
|
+
bounding_sphere=shape(
|
|
80
|
+
optimize.fminbound(
|
|
81
|
+
lambda x: -shape(x), 0.0, 2.0*np.pi, xtol=1e-10
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def sample(
|
|
87
|
+
self,
|
|
88
|
+
strategy: SamplingStrategy = None,
|
|
89
|
+
context: Optional[SamplingContext] = None
|
|
90
|
+
) -> NDArray:
|
|
91
|
+
"""
|
|
92
|
+
Returns an `NDArray` as returned by the provided
|
|
93
|
+
`SamplingStrategy`. The default `SamplingStrategy` corresponds
|
|
94
|
+
to `EquidistantSampling`. The default `SamplingContext` is
|
|
95
|
+
passed into the provided strategy uses a domain of `[0, np.pi]`
|
|
96
|
+
and otherwise the context's default values.
|
|
97
|
+
|
|
98
|
+
Keyword arguments:
|
|
99
|
+
strategy -- a `SamplingStrategy` used to sample the shape
|
|
100
|
+
(default `None`)
|
|
101
|
+
context -- a `SamplingContext` passed to the provided
|
|
102
|
+
`SamplingStrategy`
|
|
103
|
+
(default `None`)
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
if strategy is None and context is None:
|
|
107
|
+
return self._representations.sampled[0]
|
|
108
|
+
|
|
109
|
+
return (strategy or self._default_sampling_strategy).sample(
|
|
110
|
+
self._representations.ground_truth,
|
|
111
|
+
context=context or self._default_sampling_context
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def identifier(self) -> str:
|
|
116
|
+
"""Getter for property `identifier`."""
|
|
117
|
+
return str(self._identifier)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def shape(self) -> Shape:
|
|
121
|
+
"""Getter for property `shape`."""
|
|
122
|
+
return self._representations.ground_truth
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def lod0(self) -> NDArray:
|
|
126
|
+
"""Getter for property `lod0`."""
|
|
127
|
+
return self._representations.sampled[0]
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def radius(self) -> str:
|
|
131
|
+
"""Getter for property `radius` (bounding sphere)."""
|
|
132
|
+
return self._representations.bounding_sphere
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def diameter(self) -> str:
|
|
136
|
+
"""Getter for property `diameter` (bounding sphere)."""
|
|
137
|
+
return 2*self._representations.bounding_sphere
|
|
138
|
+
|
|
139
|
+
def __str__(self):
|
|
140
|
+
return f"<grain {self._identifier}>"
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# TODO: derived classes for different types: spherical, super, fourier
|
|
144
|
+
class SphericalGrain(Grain):
|
|
145
|
+
...
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class SuperGrain(Grain):
|
|
149
|
+
...
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class FourierGrain(Grain):
|
|
153
|
+
...
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def duplicate_grain(grain: Grain) -> Grain:
|
|
157
|
+
...
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def generate_batch(grain: Grain) -> list[Grain]:
|
|
161
|
+
...
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: granular
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: python library for the numerical simulation of granular materials
|
|
5
|
+
Home-page: https://pypi.org/project/granular/
|
|
6
|
+
Author: Steffen Richters-Finger
|
|
7
|
+
Author-email: srichters@uni-muenster.de
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Source, https://github.com/RichtersFinger/granular
|
|
10
|
+
Platform: UNKNOWN
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
|
|
24
|
+
# granular
|
|
25
|
+
library for the numerical simulation of granular materials
|
|
26
|
+
|
|
27
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
granular/__init__.py
|
|
5
|
+
granular/geometry.py
|
|
6
|
+
granular/grain.py
|
|
7
|
+
granular.egg-info/PKG-INFO
|
|
8
|
+
granular.egg-info/SOURCES.txt
|
|
9
|
+
granular.egg-info/dependency_links.txt
|
|
10
|
+
granular.egg-info/requires.txt
|
|
11
|
+
granular.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
granular
|
granular-0.0.0/setup.cfg
ADDED
granular-0.0.0/setup.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from setuptools import setup
|
|
3
|
+
|
|
4
|
+
# read contents of README
|
|
5
|
+
long_description = \
|
|
6
|
+
(Path(__file__).parent / "README.md").read_text(encoding="utf8")
|
|
7
|
+
|
|
8
|
+
# read contents of requirements.txt
|
|
9
|
+
requirements = \
|
|
10
|
+
(Path(__file__).parent / "requirements.txt") \
|
|
11
|
+
.read_text(encoding="utf8") \
|
|
12
|
+
.strip() \
|
|
13
|
+
.split("\n")
|
|
14
|
+
|
|
15
|
+
setup(
|
|
16
|
+
version="0.0.0",
|
|
17
|
+
name="granular",
|
|
18
|
+
description="python library for the numerical simulation of granular materials",
|
|
19
|
+
long_description=long_description,
|
|
20
|
+
long_description_content_type="text/markdown",
|
|
21
|
+
author="Steffen Richters-Finger",
|
|
22
|
+
author_email="srichters@uni-muenster.de",
|
|
23
|
+
license="MIT",
|
|
24
|
+
license_files=("LICENSE",),
|
|
25
|
+
url="https://pypi.org/project/granular/",
|
|
26
|
+
project_urls={
|
|
27
|
+
"Source": "https://github.com/RichtersFinger/granular"
|
|
28
|
+
},
|
|
29
|
+
python_requires=">=3.10",
|
|
30
|
+
install_requires=requirements,
|
|
31
|
+
packages=[
|
|
32
|
+
"granular",
|
|
33
|
+
],
|
|
34
|
+
classifiers=[
|
|
35
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
36
|
+
"Intended Audience :: Science/Research",
|
|
37
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
38
|
+
"License :: OSI Approved :: MIT License",
|
|
39
|
+
"Programming Language :: Python :: 3",
|
|
40
|
+
"Programming Language :: Python :: 3.10",
|
|
41
|
+
"Programming Language :: Python :: 3.11",
|
|
42
|
+
"Programming Language :: Python :: 3.12",
|
|
43
|
+
"Typing :: Typed",
|
|
44
|
+
],
|
|
45
|
+
)
|