patme 0.4.4__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.

Potentially problematic release.


This version of patme might be problematic. Click here for more details.

Files changed (46) hide show
  1. patme/__init__.py +52 -0
  2. patme/buildtools/__init__.py +7 -0
  3. patme/buildtools/rce_releasecreator.py +336 -0
  4. patme/buildtools/release.py +26 -0
  5. patme/femtools/__init__.py +5 -0
  6. patme/femtools/abqmsgfilechecker.py +137 -0
  7. patme/femtools/fecall.py +1092 -0
  8. patme/geometry/__init__.py +0 -0
  9. patme/geometry/area.py +124 -0
  10. patme/geometry/coordinatesystem.py +635 -0
  11. patme/geometry/intersect.py +284 -0
  12. patme/geometry/line.py +183 -0
  13. patme/geometry/misc.py +420 -0
  14. patme/geometry/plane.py +464 -0
  15. patme/geometry/rotate.py +244 -0
  16. patme/geometry/scale.py +152 -0
  17. patme/geometry/shape2d.py +50 -0
  18. patme/geometry/transformations.py +1831 -0
  19. patme/geometry/translate.py +139 -0
  20. patme/mechanics/__init__.py +4 -0
  21. patme/mechanics/loads.py +435 -0
  22. patme/mechanics/material.py +1260 -0
  23. patme/service/__init__.py +7 -0
  24. patme/service/decorators.py +85 -0
  25. patme/service/duration.py +96 -0
  26. patme/service/exceptionhook.py +104 -0
  27. patme/service/exceptions.py +36 -0
  28. patme/service/io/__init__.py +3 -0
  29. patme/service/io/basewriter.py +122 -0
  30. patme/service/logger.py +375 -0
  31. patme/service/mathutils.py +108 -0
  32. patme/service/misc.py +71 -0
  33. patme/service/moveimports.py +217 -0
  34. patme/service/stringutils.py +419 -0
  35. patme/service/systemutils.py +290 -0
  36. patme/sshtools/__init__.py +3 -0
  37. patme/sshtools/cara.py +435 -0
  38. patme/sshtools/clustercaller.py +420 -0
  39. patme/sshtools/facluster.py +350 -0
  40. patme/sshtools/sshcall.py +168 -0
  41. patme-0.4.4.dist-info/LICENSE +21 -0
  42. patme-0.4.4.dist-info/LICENSES/MIT.txt +9 -0
  43. patme-0.4.4.dist-info/METADATA +168 -0
  44. patme-0.4.4.dist-info/RECORD +46 -0
  45. patme-0.4.4.dist-info/WHEEL +4 -0
  46. patme-0.4.4.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,284 @@
1
+ # Copyright (C) 2020 Deutsches Zentrum fuer Luft- und Raumfahrt(DLR, German Aerospace Center) <www.dlr.de>
2
+ # SPDX-FileCopyrightText: 2022 German Aerospace Center (DLR)
3
+ #
4
+ # SPDX-License-Identifier: MIT
5
+ """
6
+ Created on Sep 21, 2011
7
+
8
+ @author: hein_fa
9
+ """
10
+
11
+ import math
12
+
13
+ import numpy as np
14
+ from numpy.linalg import inv
15
+
16
+ from patme import epsilon
17
+ from patme.geometry.line import Line
18
+ from patme.geometry.misc import areVectorsParallel, getPointDistanceToLine
19
+ from patme.service.exceptions import ImproperParameterError, InternalError
20
+
21
+
22
+ class Intersection:
23
+ """
24
+ This class is intended to provide the functionality to intersect two linear curves with each other
25
+ and to return the corresponding intersection point. The calculation of the intersection is limited to 2D
26
+ due to the fact that spars, ribs and stringer are located within the areodynamic loft. Therefore, they
27
+ categorical intersect each other. That means the z-coordinate of the points is dispensable for the
28
+ intersection calculation. The first two position vectors from the first linear curve and the second two
29
+ position vectors establish the second linear curve, respectively.
30
+
31
+
32
+ """
33
+
34
+ @staticmethod
35
+ def getIntersection(lines=None, areas=None, plane="xy"):
36
+ """doc"""
37
+ if lines is None:
38
+ lines = []
39
+ if areas is None:
40
+ areas = []
41
+
42
+ if len(lines) == 2:
43
+ return Intersection._intersectLines(*lines, plane=plane)
44
+ elif len(areas) == 2:
45
+ return Intersection._intersectAreas(*areas)
46
+ elif len(lines) == 1 and len(areas) == 1:
47
+ return Intersection._intersectLineArea(lines[0], areas[0])
48
+ else:
49
+ raise ImproperParameterError(
50
+ "The specified parameters don't fit the specification of intersection instance"
51
+ )
52
+
53
+ @staticmethod
54
+ def _intersectLines(line1, line2, plane="xy"):
55
+ """This method generates vectorial equations from specified initial vectors (vector1, ...vector4).
56
+ Afterwards, these two functions are intersected with each other. The resulting intersection point will be returned as result of this method.
57
+ The generated equations will look like: f: xVector = aVectror+phi*bVector ; g: xVector = cVectror+chi*dVector. Subsequently, the xVector of
58
+ f will be equalized with the xVector of g. The resulting coefficient matrix will be inverted and from left multiplied by the difference of aVector and cVector.
59
+ Following system of equation should appear:
60
+ aVector = vector1 ; bVector = vector2-vector1
61
+ cVector = vector3 ; dVector = vector4-vector3
62
+
63
+ |- -| |- -| |- -|
64
+ |bVector[0] -cVector[0]| |phi| |dVector[0]-aVector[0]|
65
+ |bVector[1] -cVector[1]| * |chi| = |dVector[1]-aVector[1]|
66
+ |- -| |- -| |- -|
67
+ ->coeffMatrix ->vector function parameters ->difference of position vector
68
+
69
+ >>> from patme.geometry.translate import Translation
70
+ >>> line1 = Line(Translation( [0., 0., 0.] ), Translation( [1., 0., 0.] ))
71
+ >>> line2 = Line(Translation( [0., 0., 0.] ), Translation( [0., 1., 0.] ))
72
+ >>> Intersection._intersectLines(line1, line2)
73
+ Translation([0., 0., 0.])
74
+ >>> line2 = Line(Translation( [0., 0., 0.] ), Translation( [0., 0., 1.] ))
75
+ >>> Intersection._intersectLines(line1, line2, plane='xz')
76
+ Translation([0., 0., 0.])
77
+ >>> line2 = Line(Translation( [0., 0., 0.] ), Translation( [1., 0., 0.] ))
78
+ >>> line1 is Intersection._intersectLines(line1, line2)
79
+ True
80
+ >>> line2 = Line(Translation( [0., 1., 0.] ), Translation( [1., 1., 0.] ))
81
+ >>> print(Intersection._intersectLines(line1, line2))
82
+ None
83
+ """
84
+ if plane not in ["xy", "xz", "yz"]:
85
+ raise ImproperParameterError("plane not implemented: " + plane)
86
+ else:
87
+ coeff11 = getattr(line1.p2, plane[0]) - getattr(line1.p1, plane[0])
88
+ coeff21 = getattr(line1.p2, plane[1]) - getattr(line1.p1, plane[1])
89
+ coeff12 = getattr(line2.p2, plane[0]) - getattr(line2.p1, plane[0])
90
+ coeff22 = getattr(line2.p2, plane[1]) - getattr(line2.p1, plane[1])
91
+
92
+ if np.linalg.norm(np.cross([coeff11, coeff21], [coeff12, coeff22])) < epsilon:
93
+ # line is parallel
94
+ distance = getPointDistanceToLine(line1.p1, line1.p2, line2.p1)
95
+ if distance < epsilon:
96
+ return line1
97
+ else:
98
+ return None
99
+
100
+ try:
101
+ coeffMatrix = np.array([[coeff11, -coeff12], [coeff21, -coeff22]])
102
+ coeffMatrixInv = inv(coeffMatrix)
103
+ except:
104
+ raise InternalError(
105
+ "Can't calculate intersection! The matrix to be inverted is singular! Either the two vectors are parallel or the specified data is corrupted."
106
+ )
107
+
108
+ # ---CALCULATE UNKNOWN PARAMETERS
109
+ if plane == "xy":
110
+ vec = np.dot(coeffMatrixInv, (line2.p1 - line1.p1)[:2])
111
+ elif plane == "xz":
112
+ vec = np.dot(coeffMatrixInv, np.array([(line2.p1 - line1.p1)[0], (line2.p1 - line1.p1)[2]]))
113
+ elif plane == "yz":
114
+ vec = np.dot(coeffMatrixInv, (line2.p1 - line1.p1)[1:])
115
+
116
+ # ---USE ONE OF THE UNKNOWN PARAMETERS TO CALCULATE THE INTERSECTION POINT
117
+ return line1.p1 + vec[0] * (line1.p2 - line1.p1)
118
+
119
+ @staticmethod
120
+ def intersectLinesMethod2d(line1, line2):
121
+ """method returns the intersection of two 2D lines with wing positions in xsi,eta coordinate system
122
+
123
+ The coordinates are in xy-plane. It can also handle lines that are parallel
124
+
125
+ returns a tuple: (xi, yi, valid, r, s), where
126
+ (xi, yi) is the intersection
127
+ r is the scalar multiple such that (xi,yi) = pt1 + r*(pt2-pt1)
128
+ s is the scalar multiple such that (xi,yi) = pt1 + s*(ptB-ptA)
129
+ valid == 0 if there are 0 or inf. intersections (invalid)
130
+ valid == 1 if it has a unique intersection ON the segment"""
131
+
132
+ # source: https://www.cs.hmc.edu/ACM/lectures/intersections.html
133
+ # method is used for spar crossing. _intersectLines method should be investigated if
134
+ # it can be used instead of this method for this purpose
135
+
136
+ # the first line is pt1 + r*(pt2-pt1)
137
+ # in component form:
138
+ x1, y1 = line1.p1.x, line1.p1.y
139
+ x2, y2 = line1.p2.x, line1.p2.y
140
+ dx1 = x2 - x1
141
+ dy1 = y2 - y1
142
+
143
+ # the second line is ptA + s*(ptB-ptA)
144
+ x, y = line2.p1.x, line2.p1.y
145
+ xB, yB = line2.p2.x, line2.p2.y
146
+
147
+ dx = xB - x
148
+ dy = yB - y
149
+
150
+ # we need to find the (typically unique) values of r and s
151
+ # that will satisfy
152
+ #
153
+ # (x1, y1) + r(dx1, dy1) = (x, y) + s(dx, dy)
154
+ #
155
+ # which is the same as
156
+ #
157
+ # [ dx1 -dx ][ r ] = [ x-x1 ]
158
+ # [ dy1 -dy ][ s ] = [ y-y1 ]
159
+ #
160
+ # whose solution is
161
+ #
162
+ # [ r ] = _1_ [ -dy dx ] [ x-x1 ]
163
+ # [ s ] = DET [ -dy1 dx1 ] [ y-y1 ]
164
+ #
165
+ # where DET = (-dx1 * dy + dy1 * dx)
166
+ #
167
+ # if DET is too small, they're parallel
168
+ #
169
+ DET = -dx1 * dy + dy1 * dx
170
+
171
+ if math.fabs(DET) < epsilon:
172
+ return (0, 0, 0, 0, 0)
173
+
174
+ # find the scalar amount along the "self" segment
175
+ r = (1.0 / DET) * (-dy * (x - x1) + dx * (y - y1))
176
+
177
+ # find the scalar amount along the input line
178
+ s = (1.0 / DET) * (-dy1 * (x - x1) + dx1 * (y - y1))
179
+
180
+ # return the average of the two descriptions
181
+ xi = (x1 + r * dx1 + x + s * dx) / 2.0
182
+ yi = (y1 + r * dy1 + y + s * dy) / 2.0
183
+ return (xi, yi, 1, r, s)
184
+
185
+ @staticmethod
186
+ def _intersectAreas(plane1, plane2):
187
+ """Calculate intersection of two planes.
188
+
189
+ :return: If the planes cross each other, a line instance is returned. If the
190
+ planes are coincident, the first plane is returned. If the planes have the same
191
+ normal direction but the planes do not coincide, None is returned.
192
+
193
+ >>> from patme.geometry.plane import Plane
194
+ >>> plane1, plane2 = Plane(), Plane()
195
+ >>> plane1 = plane1.generatePlane([0,0,0], planeNormalVector = [0,0,1])
196
+ >>> plane2 = plane2.generatePlane([0,0,0], planeNormalVector = [0,1,0])
197
+ >>> print(Intersection._intersectAreas(plane1, plane2))
198
+ [0. 0. 0.], [-1. 0. 0.]
199
+ >>> plane2 = plane2.generatePlane([0,0,0], planeNormalVector = [0,0,1])
200
+ >>> plane1 is Intersection._intersectAreas(plane1, plane2)
201
+ True
202
+ >>> plane2 = plane2.generatePlane([0,0,1], planeNormalVector = [0,0,1])
203
+ >>> print(Intersection._intersectAreas(plane1, plane2))
204
+ None
205
+ """
206
+ # get direction of resulting line
207
+ directionVector = np.cross(plane1.planeNormalVector, plane2.planeNormalVector)
208
+ if np.linalg.norm(directionVector) < epsilon:
209
+ # normal vectors are parallel to each other
210
+ if plane1.getDistanceToPoint(plane2.planePositioningVector) < epsilon:
211
+ # planes are coincident
212
+ return plane1
213
+ else:
214
+ return None
215
+
216
+ # =======================================================================
217
+ # get base vector of the desired line
218
+ # This is done by using the plane orientation vector that is not parallel to the other plane.
219
+ # Then the intersection of a line with this orientation vector and the other plane is calculated.
220
+ # =======================================================================
221
+
222
+ lineToIntersect = Line(
223
+ plane1.planePositioningVector,
224
+ plane1.planePositioningVector + plane1.planeOrientationVector1,
225
+ )
226
+ intersectionPoint = Intersection.getIntersection([lineToIntersect], [plane2])
227
+ if intersectionPoint is None or intersectionPoint is lineToIntersect:
228
+ # orientation vector1 is parallel to the other plane
229
+ lineToIntersect = Line(
230
+ plane1.planePositioningVector,
231
+ plane1.planePositioningVector + plane1.planeOrientationVector2,
232
+ )
233
+ intersectionPoint = Intersection.getIntersection([lineToIntersect], [plane2])
234
+
235
+ return Line(intersectionPoint, (intersectionPoint + directionVector))
236
+
237
+ @staticmethod
238
+ def _intersectLineArea(line, plane):
239
+ """doc
240
+
241
+ >>> from patme.geometry.translate import Translation
242
+ >>> from patme.geometry.plane import Plane
243
+ >>> plane = Plane()
244
+ >>> plane = plane.generatePlane([0,0,0], planeNormalVector = [0,0,1])
245
+ >>> Intersection._intersectLineArea(Line(Translation([0,0,0]),Translation([0,0,1])), plane)
246
+ Translation([0., 0., 0.])
247
+ >>> print(Intersection._intersectLineArea(Line(Translation([0,0,1]),Translation([1,0,1])), plane))
248
+ None
249
+ >>> line = Line(Translation([0,0,0]),Translation([1,0,0]))
250
+ >>> line is Intersection._intersectLineArea(line, plane)
251
+ True
252
+ """
253
+ # check if line is parallel to plane
254
+ usedVector = (
255
+ plane.planeOrientationVector2
256
+ if areVectorsParallel(plane.planeOrientationVector1, line.vector)
257
+ else plane.planeOrientationVector1
258
+ )
259
+ crossProduct = np.cross(usedVector, line.vector)
260
+
261
+ if areVectorsParallel(crossProduct, plane.planeNormalVector):
262
+ # line is parallel to plane
263
+ distance = np.linalg.norm(
264
+ np.dot(plane.planeNormalVector, (line.p1 - plane.planePositioningVector))
265
+ ) / np.linalg.norm(plane.planeNormalVector)
266
+ if -1.0 * epsilon <= distance <= epsilon:
267
+ # Line lays within plane. Infinite number of intersection point calculable.
268
+ return line
269
+ else:
270
+ return None
271
+ else:
272
+ phi = np.dot(plane.planeNormalVector, (plane.planePositioningVector - line.p1)) / np.dot(
273
+ plane.planeNormalVector, line.vector
274
+ )
275
+ intersectionPoint = line.p1 + phi * line.vector
276
+
277
+ return intersectionPoint
278
+
279
+
280
+ if __name__ == "__main__":
281
+
282
+ import doctest
283
+
284
+ doctest.testmod()
patme/geometry/line.py ADDED
@@ -0,0 +1,183 @@
1
+ # Copyright (C) 2020 Deutsches Zentrum fuer Luft- und Raumfahrt(DLR, German Aerospace Center) <www.dlr.de>
2
+ # SPDX-FileCopyrightText: 2022 German Aerospace Center (DLR)
3
+ #
4
+ # SPDX-License-Identifier: MIT
5
+
6
+
7
+ from operator import attrgetter
8
+
9
+ import numpy as np
10
+
11
+ from patme import epsilon
12
+ from patme.geometry.translate import Translation
13
+ from patme.service.exceptions import InternalError
14
+
15
+
16
+ class Lines(list):
17
+ """doc"""
18
+
19
+ def sort(self, cmp=None, key=None, reverse=False, attribute=None):
20
+ """doc"""
21
+ if key == None:
22
+ try:
23
+ # make attribute an iterable item if needed
24
+ a = attribute[0]
25
+ if isinstance(attribute, str):
26
+ raise
27
+ except:
28
+ attribute = [attribute]
29
+ key = attrgetter(*attribute)
30
+ list.sort(self, key=key, reverse=reverse)
31
+
32
+
33
+ class Line:
34
+ """classdocs"""
35
+
36
+ # Attention: when changing parameters, please also edit them in model.mechanicalproperties.Beam.__init__
37
+ def __init__(self, point1=None, point2=None, lineID=None, doZeroLengthCheck=True):
38
+ """Creates a line based on two points
39
+
40
+ :param point1: first point, should be instance of model.geometry.translate.Translation
41
+ :param point2: first point, should be instance of model.geometry.translate.Translation
42
+ :param lineID: id of the point used in FEM
43
+ :param connectVertex: Flag if common neighbors of point1 and point2 should be connected with the new line
44
+ :param doZeroLengthCheck: geometrical lines should not have a length of zero. Hence an exception is risen in this case.
45
+ If a length of zero is on purpose (e.g. to constrain two nodes), this flag should be set to False
46
+ """
47
+ # init ac graph vertex
48
+ if point1 is point2:
49
+ raise InternalError("The same points are given as point1 and point2.")
50
+
51
+ self._keypoints = []
52
+ if point1 is not None and point2 is not None:
53
+ self._keypoints = [point1, point2]
54
+
55
+ # check if lines have a length higher than zero
56
+ if doZeroLengthCheck and self._keypoints and self.length < epsilon:
57
+ raise InternalError(f"The given line has a length of zero! line, length: {lineID}, {self.length}")
58
+
59
+ self.id = lineID
60
+ self.cutout = None
61
+
62
+ def copy(self, old2NewDict, copyProperties):
63
+ """returns a copy of this instance.
64
+ :param old2NewDict: references attributes of self with the newly created instances to
65
+ reestablish the connections between instances
66
+ :param copyProperties: Flag if sheetproperties and profiles should be copied or
67
+ if the references should be kept.
68
+ """
69
+ return self.__class__(old2NewDict[self.p1], old2NewDict[self.p2], lineID=self.id)
70
+
71
+ def __str__(self):
72
+ """doc"""
73
+ if self._keypoints:
74
+ return ", ".join(str(k) for k in self._keypoints)
75
+ else:
76
+ return ""
77
+
78
+ def _getp1(self):
79
+ """doc"""
80
+ self._checkKeypoints()
81
+ return self.keypoints[0]
82
+
83
+ def _getp2(self):
84
+ """doc"""
85
+ self._checkKeypoints()
86
+ return self.keypoints[-1]
87
+
88
+ def _getxLesser(self):
89
+ """Returns the lesser x-coordinate of the two keypoints."""
90
+ self._checkKeypoints()
91
+ return min(self.p1.x, self.p2.x)
92
+
93
+ def _getyLesser(self):
94
+ """Returns the lesser y-coordinate of the two keypoints."""
95
+ self._checkKeypoints()
96
+ return min(self.p1.y, self.p2.y)
97
+
98
+ def _getzLesser(self):
99
+ """Returns the lesser z-coordinate of the two keypoints."""
100
+ self._checkKeypoints()
101
+ return min(self.p1.z, self.p2.z)
102
+
103
+ def _getxGreater(self):
104
+ """Returns the greater x-coordinate of the two keypoints."""
105
+ self._checkKeypoints()
106
+ return max(self.p1.x, self.p2.x)
107
+
108
+ def _getyGreater(self):
109
+ """Returns the greater y-coordinate of the two keypoints."""
110
+ self._checkKeypoints()
111
+ return max(self.p1.y, self.p2.y)
112
+
113
+ def _getzGreater(self):
114
+ """Returns the greater z-coordinate of the two keypoints."""
115
+ self._checkKeypoints()
116
+ return max(self.p1.z, self.p2.z)
117
+
118
+ def _getLength(self):
119
+ """doc"""
120
+ return self.p1.distance(self.p2)
121
+
122
+ def _getKeypoints(self):
123
+ """doc"""
124
+ return self._keypoints
125
+
126
+ def _checkKeypoints(self):
127
+ """This method raises an InternalError if there are not exactly 2 keypoints."""
128
+ if len(self.keypoints) != 2:
129
+ raise InternalError("There are not exactly 2 keypoints for line %s" % repr(self))
130
+
131
+ def _getVector(self):
132
+ """Retruns the vector of the line as p2=p1+vector"""
133
+ return self.p2 - self.p1
134
+
135
+ def _getNormalizedVector(self):
136
+ """Retruns the vector of the line as p2=p1+vector"""
137
+ vector = self.vector
138
+ return vector / np.linalg.norm(vector)
139
+
140
+ def _getMeanPoint(self):
141
+ """Retruns the mean of both points: mean(self.p1, self.p2)"""
142
+ return Translation(np.mean([self.p2, self.p1], 0))
143
+
144
+ keypoints = property(fget=_getKeypoints)
145
+ p1 = property(fget=_getp1)
146
+ p2 = property(fget=_getp2)
147
+ xLesser = property(fget=_getxLesser)
148
+ """Returns the lesser x-coordinate of the two keypoints."""
149
+ yLesser = property(fget=_getyLesser)
150
+ """Returns the lesser y-coordinate of the two keypoints."""
151
+ zLesser = property(fget=_getzLesser)
152
+ """Returns the lesser z-coordinate of the two keypoints."""
153
+ xGreater = property(fget=_getxGreater)
154
+ """Returns the greater x-coordinate of the two keypoints."""
155
+ yGreater = property(fget=_getyGreater)
156
+ """Returns the greater y-coordinate of the two keypoints."""
157
+ zGreater = property(fget=_getzGreater)
158
+ """Returns the greater z-coordinate of the two keypoints."""
159
+ length = property(fget=_getLength)
160
+ """Returns the length of the line (2-norm)"""
161
+ vector = property(fget=_getVector)
162
+ """Returns the vector of the line as p2=p1+vector"""
163
+ meanPoint = property(fget=_getMeanPoint)
164
+ """Returns the mean of the two points: mean(self.p1, self.p2)"""
165
+ normalizedVector = property(fget=_getNormalizedVector)
166
+ """Returns the mean of the two points: mean(self.p1, self.p2)"""
167
+
168
+
169
+ def computePointsOnBezierCurve(bezierKnots, s_vals):
170
+ """doc"""
171
+ from scipy.special import binom
172
+
173
+ npbezierKnots = np.array(bezierKnots)
174
+ degree = npbezierKnots.shape[0] - 1
175
+
176
+ kVals = np.arange(degree + 1)
177
+ tmp = np.power(1 - s_vals[:, np.newaxis], degree - kVals)
178
+ tmp2 = np.power(s_vals[:, np.newaxis], kVals)
179
+
180
+ binomStuff = binom([degree] * kVals.size, kVals)
181
+ res = binomStuff * tmp * tmp2
182
+ evaluatedPoints = np.dot(npbezierKnots.T, res.T).T
183
+ return evaluatedPoints