• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmc-dev / openmc / 12776996362

14 Jan 2025 09:49PM UTC coverage: 84.938% (+0.2%) from 84.729%
12776996362

Pull #3133

github

web-flow
Merge 0495246d9 into 549cc0973
Pull Request #3133: Kinetics parameters using Iterated Fission Probability

318 of 330 new or added lines in 10 files covered. (96.36%)

1658 existing lines in 66 files now uncovered.

50402 of 59340 relevant lines covered (84.94%)

33987813.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.17
/openmc/region.py
1
from __future__ import annotations
12✔
2
from abc import ABC, abstractmethod
12✔
3
from collections.abc import MutableSequence
12✔
4
from copy import deepcopy
12✔
5
import warnings
12✔
6

7
import numpy as np
12✔
8

9
import openmc
12✔
10
from .bounding_box import BoundingBox
12✔
11

12

13
class Region(ABC):
12✔
14
    """Region of space that can be assigned to a cell.
15

16
    Region is an abstract base class that is inherited by
17
    :class:`openmc.Halfspace`, :class:`openmc.Intersection`,
18
    :class:`openmc.Union`, and :class:`openmc.Complement`. Each of those
19
    respective classes are typically not instantiated directly but rather are
20
    created through operators of the Surface and Region classes.
21

22
    Attributes
23
    ----------
24
    bounding_box : openmc.BoundingBox
25
        Axis-aligned bounding box of the region
26

27
    """
28
    def __and__(self, other):
12✔
29
        return Intersection((self, other))
12✔
30

31
    def __or__(self, other):
12✔
32
        return Union((self, other))
12✔
33

34
    @abstractmethod
12✔
35
    def __invert__(self) -> Region:
12✔
UNCOV
36
        pass
×
37

38
    @abstractmethod
12✔
39
    def __contains__(self, point):
12✔
UNCOV
40
        pass
×
41

42
    @property
12✔
43
    @abstractmethod
12✔
44
    def bounding_box(self) -> BoundingBox:
12✔
UNCOV
45
        pass
×
46

47
    @abstractmethod
12✔
48
    def __str__(self):
12✔
UNCOV
49
        pass
×
50

51
    def __eq__(self, other):
12✔
52
        if not isinstance(other, type(self)):
12✔
53
            return False
12✔
54
        else:
55
            return str(self) == str(other)
12✔
56

57
    def get_surfaces(self, surfaces=None):
12✔
58
        """Recursively find all surfaces referenced by a region and return them
59

60
        Parameters
61
        ----------
62
        surfaces : dict, optional
63
            Dictionary mapping surface IDs to :class:`openmc.Surface` instances
64

65
        Returns
66
        -------
67
        surfaces : dict
68
            Dictionary mapping surface IDs to :class:`openmc.Surface` instances
69

70
        """
71
        if surfaces is None:
12✔
72
            surfaces = {}
12✔
73
        for region in self:
12✔
74
            surfaces = region.get_surfaces(surfaces)
12✔
75
        return surfaces
12✔
76

77
    def remove_redundant_surfaces(self, redundant_surfaces):
12✔
78
        """Recursively remove all redundant surfaces referenced by this region
79

80
        .. versionadded:: 0.12
81

82
        Parameters
83
        ----------
84
        redundant_surfaces : dict
85
            Dictionary mapping redundant surface IDs to class:`openmc.Surface`
86
            instances that should replace them.
87

88
        """
89
        for region in self:
12✔
90
            region.remove_redundant_surfaces(redundant_surfaces)
12✔
91

92
    @staticmethod
12✔
93
    def from_expression(expression, surfaces):
12✔
94
        """Generate a region given an infix expression.
95

96
        Parameters
97
        ----------
98
        expression : str
99
            Boolean expression relating surface half-spaces. The possible
100
            operators are union '|', intersection ' ', and complement '~'. For
101
            example, '(1 -2) | 3 ~(4 -5)'.
102
        surfaces : dict
103
            Dictionary whose keys are surface IDs that appear in the Boolean
104
            expression and whose values are Surface objects.
105

106
        """
107

108
        # Strip leading and trailing whitespace
109
        expression = expression.strip()
12✔
110

111
        # Convert the string expression into a list of tokens, i.e., operators
112
        # and surface half-spaces, representing the expression in infix
113
        # notation.
114
        i = 0
12✔
115
        i_start = -1
12✔
116
        tokens = []
12✔
117
        while i < len(expression):
12✔
118
            if expression[i] in '()|~ ':
12✔
119
                # If special character appears immediately after a non-operator,
120
                # create a token with the appropriate half-space
121
                if i_start >= 0:
12✔
122
                    j = int(expression[i_start:i])
12✔
123
                    if j < 0:
12✔
124
                        tokens.append(-surfaces[abs(j)])
12✔
125
                    else:
126
                        tokens.append(+surfaces[abs(j)])
12✔
127

128
                    # When an opening parenthesis appears after a non-operator,
129
                    # there's an implicit intersection operator between them
130
                    if expression[i] == '(':
12✔
131
                        tokens.append(' ')
12✔
132

133
                if expression[i] in '()|~':
12✔
134
                    # For everything other than intersection, add the operator
135
                    # to the list of tokens
136
                    tokens.append(expression[i])
12✔
137

138
                    # If two parentheses appear immediately adjacent to one
139
                    # another, we need an intersection between them
140
                    if expression[i:i+2] == ')(':
12✔
141
                        tokens.append(' ')
12✔
142
                else:
143
                    # Find next non-space character
144
                    while expression[i+1] == ' ':
12✔
UNCOV
145
                        i += 1
×
146

147
                    # If previous token is a halfspace or right parenthesis and
148
                    # next token is not a left parenthesis or union operator,
149
                    # that implies that the whitespace is to be interpreted as
150
                    # an intersection operator
151
                    if (i_start >= 0 or tokens[-1] == ')') and \
12✔
152
                       expression[i+1] not in ')|':
153
                        tokens.append(' ')
12✔
154

155
                i_start = -1
12✔
156
            else:
157
                # Check for invalid characters
158
                if expression[i] not in '-+0123456789':
12✔
UNCOV
159
                    raise SyntaxError(f"Invalid character '{expression[i]}' in "
×
160
                                      "expression")
161

162
                # If we haven't yet reached the start of a word, start one
163
                if i_start < 0:
12✔
164
                    i_start = i
12✔
165
            i += 1
12✔
166

167
        # If we've reached the end and we're still in a word, create a
168
        # half-space token and add it to the list
169
        if i_start >= 0:
12✔
170
            j = int(expression[i_start:])
12✔
171
            if j < 0:
12✔
172
                tokens.append(-surfaces[abs(j)])
12✔
173
            else:
174
                tokens.append(+surfaces[abs(j)])
12✔
175

176
        # The functions below are used to apply an operator to operands on the
177
        # output queue during the shunting yard algorithm.
178
        def can_be_combined(region):
12✔
179
            return isinstance(region, Complement) or hasattr(region, 'surface')
12✔
180

181
        def apply_operator(output, operator):
12✔
182
            r2 = output.pop()
12✔
183
            if operator == ' ':
12✔
184
                r1 = output.pop()
12✔
185
                if isinstance(r1, Intersection):
12✔
186
                    r1 &= r2
12✔
187
                    output.append(r1)
12✔
188
                elif isinstance(r2, Intersection) and can_be_combined(r1):
12✔
UNCOV
189
                    r2.insert(0, r1)
×
UNCOV
190
                    output.append(r2)
×
191
                else:
192
                    output.append(r1 & r2)
12✔
193
            elif operator == '|':
12✔
194
                r1 = output.pop()
12✔
195
                if isinstance(r1, Union):
12✔
196
                    r1 |= r2
12✔
197
                    output.append(r1)
12✔
198
                elif isinstance(r2, Union) and can_be_combined(r1):
12✔
UNCOV
199
                    r2.insert(0, r1)
×
UNCOV
200
                    output.append(r2)
×
201
                else:
202
                    output.append(r1 | r2)
12✔
203
            elif operator == '~':
12✔
204
                output.append(~r2)
12✔
205

206
        # The following is an implementation of the shunting yard algorithm to
207
        # generate an abstract syntax tree for the region expression.
208
        output = []
12✔
209
        stack = []
12✔
210
        precedence = {'|': 1, ' ': 2, '~': 3}
12✔
211
        associativity = {'|': 'left', ' ': 'left', '~': 'right'}
12✔
212
        for token in tokens:
12✔
213
            if token in (' ', '|', '~'):
12✔
214
                # Normal operators
215
                while stack:
12✔
216
                    op = stack[-1]
12✔
217
                    if (op not in ('(', ')') and
12✔
218
                        ((associativity[token] == 'right' and
219
                          precedence[token] < precedence[op]) or
220
                         (associativity[token] == 'left' and
221
                          precedence[token] <= precedence[op]))):
222
                        apply_operator(output, stack.pop())
12✔
223
                    else:
224
                        break
12✔
225
                stack.append(token)
12✔
226
            elif token == '(':
12✔
227
                # Left parentheses
228
                stack.append(token)
12✔
229
            elif token == ')':
12✔
230
                # Right parentheses
231
                while stack[-1] != '(':
12✔
232
                    apply_operator(output, stack.pop())
12✔
233
                    if len(stack) == 0:
12✔
UNCOV
234
                        raise SyntaxError('Mismatched parentheses in '
×
235
                                          'region specification.')
236
                stack.pop()
12✔
237
            else:
238
                # Surface halfspaces
239
                output.append(token)
12✔
240
        while stack:
12✔
241
            if stack[-1] in '()':
12✔
UNCOV
242
                raise SyntaxError('Mismatched parentheses in region '
×
243
                                  'specification.')
244
            apply_operator(output, stack.pop())
12✔
245

246
        # Since we are generating an abstract syntax tree rather than a reverse
247
        # Polish notation expression, the output queue should have a single item
248
        # at the end
249
        return output[0]
12✔
250

251
    def clone(self, memo=None):
12✔
252
        """Create a copy of this region - each of the surfaces in the
253
        region's nodes will be cloned and will have new unique IDs.
254

255
        Parameters
256
        ----------
257
        memo : dict or None
258
            A nested dictionary of previously cloned objects. This parameter
259
            is used internally and should not be specified by the user.
260

261
        Returns
262
        -------
263
        clone : openmc.Region
264
            The clone of this region
265

266
        """
267

268
        if memo is None:
12✔
269
            memo = {}
12✔
270

271
        clone = deepcopy(self)
12✔
272
        clone[:] = [n.clone(memo) for n in self]
12✔
273
        return clone
12✔
274

275
    def translate(self, vector, inplace=False, memo=None):
12✔
276
        """Translate region in given direction
277

278
        Parameters
279
        ----------
280
        vector : iterable of float
281
            Direction in which region should be translated
282
        inplace : bool
283
            Whether or not to return a region based on new surfaces or one based
284
            on the original surfaces that have been modified.
285

286
            .. versionadded:: 0.13.1
287
        memo : dict or None
288
            Dictionary used for memoization. This parameter is used internally
289
            and should not be specified by the user.
290

291
        Returns
292
        -------
293
        openmc.Region
294
            Translated region
295

296
        """
297

298
        if memo is None:
12✔
299
            memo = {}
12✔
300
        return type(self)(n.translate(vector, inplace, memo) for n in self)
12✔
301

302
    def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False,
12✔
303
               memo=None):
304
        r"""Rotate surface by angles provided or by applying matrix directly.
305

306
        .. versionadded:: 0.12
307

308
        Parameters
309
        ----------
310
        rotation : 3-tuple of float, or 3x3 iterable
311
            A 3-tuple of angles :math:`(\phi, \theta, \psi)` in degrees where
312
            the first element is the rotation about the x-axis in the fixed
313
            laboratory frame, the second element is the rotation about the
314
            y-axis in the fixed laboratory frame, and the third element is the
315
            rotation about the z-axis in the fixed laboratory frame. The
316
            rotations are active rotations. Additionally a 3x3 rotation matrix
317
            can be specified directly either as a nested iterable or array.
318
        pivot : iterable of float, optional
319
            (x, y, z) coordinates for the point to rotate about. Defaults to
320
            (0., 0., 0.)
321
        order : str, optional
322
            A string of 'x', 'y', and 'z' in some order specifying which
323
            rotation to perform first, second, and third. Defaults to 'xyz'
324
            which means, the rotation by angle :math:`\phi` about x will be
325
            applied first, followed by :math:`\theta` about y and then
326
            :math:`\psi` about z. This corresponds to an x-y-z extrinsic
327
            rotation as well as a z-y'-x'' intrinsic rotation using Tait-Bryan
328
            angles :math:`(\phi, \theta, \psi)`.
329
        inplace : bool
330
            Whether or not to return a new instance of Surface or to modify the
331
            coefficients of this Surface in place. Defaults to False.
332
        memo : dict or None
333
            Dictionary used for memoization
334

335
        Returns
336
        -------
337
        openmc.Region
338
            Translated region
339

340
        """
341
        if memo is None:
12✔
342
            memo = {}
12✔
343
        return type(self)(n.rotate(rotation, pivot=pivot, order=order,
12✔
344
                                   inplace=inplace, memo=memo) for n in self)
345

346
    def plot(self, *args, **kwargs):
12✔
347
        """Display a slice plot of the region.
348

349
        .. versionadded:: 0.15.0
350

351
        Parameters
352
        ----------
353
        origin : iterable of float
354
            Coordinates at the origin of the plot. If left as None then the
355
            bounding box center will be used to attempt to ascertain the origin.
356
            Defaults to (0, 0, 0) if the bounding box is not finite
357
        width : iterable of float
358
            Width of the plot in each basis direction. If left as none then the
359
            bounding box width will be used to attempt to ascertain the plot
360
            width. Defaults to (10, 10) if the bounding box is not finite
361
        pixels : Iterable of int or int
362
            If iterable of ints provided, then this directly sets the number of
363
            pixels to use in each basis direction. If int provided, then this
364
            sets the total number of pixels in the plot and the number of pixels
365
            in each basis direction is calculated from this total and the image
366
            aspect ratio.
367
        basis : {'xy', 'xz', 'yz'}
368
            The basis directions for the plot
369
        seed : int
370
            Seed for the random number generator
371
        openmc_exec : str
372
            Path to OpenMC executable.
373
        axes : matplotlib.Axes
374
            Axes to draw to
375
        outline : bool
376
            Whether outlines between color boundaries should be drawn
377
        axis_units : {'km', 'm', 'cm', 'mm'}
378
            Units used on the plot axis
379
        **kwargs
380
            Keyword arguments passed to :func:`matplotlib.pyplot.imshow`
381

382
        Returns
383
        -------
384
        matplotlib.axes.Axes
385
            Axes containing resulting image
386

387
        """
388
        for key in ('color_by', 'colors', 'legend', 'legend_kwargs'):
12✔
389
            if key in kwargs:
12✔
UNCOV
390
                warnings.warn(f"The '{key}' argument is present but won't be applied in a region plot")
×
391

392
        # Create cell while not perturbing use of autogenerated IDs
393
        next_id = openmc.Cell.next_id
12✔
394
        c = openmc.Cell(region=self)
12✔
395
        openmc.Cell.used_ids.remove(c.id)
12✔
396
        openmc.Cell.next_id = next_id
12✔
397
        return c.plot(*args, **kwargs)
12✔
398

399

400
class Intersection(Region, MutableSequence):
12✔
401
    r"""Intersection of two or more regions.
402

403
    Instances of Intersection are generally created via the & operator applied
404
    to two instances of :class:`openmc.Region`. This is illustrated in the
405
    following example:
406

407
    >>> equator = openmc.ZPlane(z0=0.0)
408
    >>> earth = openmc.Sphere(r=637.1e6)
409
    >>> northern_hemisphere = -earth & +equator
410
    >>> southern_hemisphere = -earth & -equator
411
    >>> type(northern_hemisphere)
412
    <class 'openmc.region.Intersection'>
413

414
    Instances of this class behave like a mutable sequence, e.g., they can be
415
    indexed and have an append() method.
416

417
    Parameters
418
    ----------
419
    nodes : iterable of openmc.Region
420
        Regions to take the intersection of
421

422
    Attributes
423
    ----------
424
    bounding_box : openmc.BoundingBox
425
        Axis-aligned bounding box of the region
426

427
    """
428

429
    def __init__(self, nodes):
12✔
430
        self._nodes = list(nodes)
12✔
431
        for node in nodes:
12✔
432
            if not isinstance(node, Region):
12✔
433
                raise ValueError('Intersection operands must be of type Region')
12✔
434

435
    def __and__(self, other):
12✔
436
        new = Intersection(self)
12✔
437
        new &= other
12✔
438
        return new
12✔
439

440
    def __iand__(self, other):
12✔
441
        if isinstance(other, Intersection):
12✔
442
            self.extend(other)
12✔
443
        else:
444
            self.append(other)
12✔
445
        return self
12✔
446

447
    def __invert__(self) -> Union:
12✔
448
        return Union(~n for n in self)
12✔
449

450
    # Implement mutable sequence protocol by delegating to list
451
    def __getitem__(self, key):
12✔
452
        return self._nodes[key]
12✔
453

454
    def __setitem__(self, key, value):
12✔
455
        self._nodes[key] = value
12✔
456

457
    def __delitem__(self, key):
12✔
UNCOV
458
        del self._nodes[key]
×
459

460
    def __len__(self):
12✔
461
        return len(self._nodes)
12✔
462

463
    def insert(self, index, value):
12✔
464
        self._nodes.insert(index, value)
12✔
465

466
    def __contains__(self, point):
12✔
467
        """Check whether a point is contained in the region.
468

469
        Parameters
470
        ----------
471
        point : 3-tuple of float
472
            Cartesian coordinates, :math:`(x',y',z')`, of the point
473

474
        Returns
475
        -------
476
        bool
477
            Whether the point is in the region
478

479
        """
480
        return all(point in n for n in self)
12✔
481

482
    def __str__(self):
12✔
483
        return '(' + ' '.join(map(str, self)) + ')'
12✔
484

485
    @property
12✔
486
    def bounding_box(self) -> BoundingBox:
12✔
487
        box = BoundingBox.infinite()
12✔
488
        for n in self:
12✔
489
            box &= n.bounding_box
12✔
490
        return box
12✔
491

492

493
class Union(Region, MutableSequence):
12✔
494
    r"""Union of two or more regions.
495

496
    Instances of Union are generally created via the | operator applied to two
497
    instances of :class:`openmc.Region`. This is illustrated in the following
498
    example:
499

500
    >>> s1 = openmc.ZPlane(z0=0.0)
501
    >>> s2 = openmc.Sphere(r=637.1e6)
502
    >>> type(-s2 | +s1)
503
    <class 'openmc.region.Union'>
504

505
    Instances of this class behave like a mutable sequence, e.g., they can be
506
    indexed and have an append() method.
507

508
    Parameters
509
    ----------
510
    nodes : iterable of openmc.Region
511
        Regions to take the union of
512

513
    Attributes
514
    ----------
515
    bounding_box : openmc.BoundingBox
516
        Axis-aligned bounding box of the region
517

518
    """
519

520
    def __init__(self, nodes):
12✔
521
        self._nodes = list(nodes)
12✔
522
        for node in nodes:
12✔
523
            if not isinstance(node, Region):
12✔
524
                raise ValueError('Union operands must be of type Region')
12✔
525

526
    def __or__(self, other):
12✔
527
        new = Union(self)
12✔
528
        new |= other
12✔
529
        return new
12✔
530

531
    def __ior__(self, other):
12✔
532
        if isinstance(other, Union):
12✔
533
            self.extend(other)
12✔
534
        else:
535
            self.append(other)
12✔
536
        return self
12✔
537

538
    def __invert__(self) -> Intersection:
12✔
539
        return Intersection(~n for n in self)
12✔
540

541
    # Implement mutable sequence protocol by delegating to list
542
    def __getitem__(self, key):
12✔
543
        return self._nodes[key]
12✔
544

545
    def __setitem__(self, key, value):
12✔
546
        self._nodes[key] = value
12✔
547

548
    def __delitem__(self, key):
12✔
UNCOV
549
        del self._nodes[key]
×
550

551
    def __len__(self):
12✔
552
        return len(self._nodes)
12✔
553

554
    def insert(self, index, value):
12✔
555
        self._nodes.insert(index, value)
12✔
556

557
    def __contains__(self, point):
12✔
558
        """Check whether a point is contained in the region.
559

560
        Parameters
561
        ----------
562
        point : 3-tuple of float
563
            Cartesian coordinates, :math:`(x',y',z')`, of the point
564

565
        Returns
566
        -------
567
        bool
568
            Whether the point is in the region
569

570
        """
571
        return any(point in n for n in self)
12✔
572

573
    def __str__(self):
12✔
574
        return '(' + ' | '.join(map(str, self)) + ')'
12✔
575

576
    @property
12✔
577
    def bounding_box(self) -> BoundingBox:
12✔
578
        bbox = BoundingBox(np.array([np.inf]*3),
12✔
579
                           np.array([-np.inf]*3))
580
        for n in self:
12✔
581
            bbox |= n.bounding_box
12✔
582
        return bbox
12✔
583

584

585
class Complement(Region):
12✔
586
    """Complement of a region.
587

588
    The Complement of an existing :class:`openmc.Region` can be created by using
589
    the ~ operator as the following example demonstrates:
590

591
    >>> xl = openmc.XPlane(-10.0)
592
    >>> xr = openmc.XPlane(10.0)
593
    >>> yl = openmc.YPlane(-10.0)
594
    >>> yr = openmc.YPlane(10.0)
595
    >>> inside_box = +xl & -xr & +yl & -yr
596
    >>> outside_box = ~inside_box
597
    >>> type(outside_box)
598
    <class 'openmc.region.Complement'>
599

600
    Parameters
601
    ----------
602
    node : openmc.Region
603
        Region to take the complement of
604

605
    Attributes
606
    ----------
607
    node : openmc.Region
608
        Regions to take the complement of
609
    bounding_box : openmc.BoundingBox
610
        Axis-aligned bounding box of the region
611

612
    """
613

614
    def __init__(self, node: Region):
12✔
615
        self.node = node
12✔
616

617
    def __contains__(self, point):
12✔
618
        """Check whether a point is contained in the region.
619

620
        Parameters
621
        ----------
622
        point : 3-tuple of float
623
            Cartesian coordinates, :math:`(x',y',z')`, of the point
624

625
        Returns
626
        -------
627
        bool
628
            Whether the point is in the region
629

630
        """
UNCOV
631
        return point not in self.node
×
632

633
    def __invert__(self) -> Region:
12✔
UNCOV
634
        return self.node
×
635

636
    def __str__(self):
12✔
UNCOV
637
        return '~' + str(self.node)
×
638

639
    @property
12✔
640
    def node(self):
12✔
UNCOV
641
        return self._node
×
642

643
    @node.setter
12✔
644
    def node(self, node):
12✔
645
        if not isinstance(node, Region):
12✔
646
            raise ValueError('Complement operand must be of type Region')
12✔
UNCOV
647
        self._node = node
×
648

649
    @property
12✔
650
    def bounding_box(self) -> BoundingBox:
12✔
UNCOV
651
        return (~self.node).bounding_box
×
652

653
    def get_surfaces(self, surfaces=None):
12✔
654
        """Recursively find and return all the surfaces referenced by the node
655

656
        Parameters
657
        ----------
658
        surfaces : dict, optional
659
            Dictionary mapping surface IDs to :class:`openmc.Surface` instances
660

661
        Returns
662
        -------
663
        surfaces : dict
664
            Dictionary mapping surface IDs to :class:`openmc.Surface` instances
665

666
        """
UNCOV
667
        if surfaces is None:
×
UNCOV
668
            surfaces = {}
×
UNCOV
669
        for region in self.node:
×
UNCOV
670
            surfaces = region.get_surfaces(surfaces)
×
UNCOV
671
        return surfaces
×
672

673
    def remove_redundant_surfaces(self, redundant_surfaces):
12✔
674
        """Recursively remove all redundant surfaces referenced by this region
675

676
        .. versionadded:: 0.12
677

678
        Parameters
679
        ----------
680
        redundant_surfaces : dict
681
            Dictionary mapping redundant surface IDs to class:`openmc.Surface`
682
            instances that should replace them.
683

684
        """
685
        for region in self.node:
×
686
            region.remove_redundant_surfaces(redundant_surfaces)
×
687

688
    def clone(self, memo=None):
12✔
UNCOV
689
        if memo is None:
×
UNCOV
690
            memo = {}
×
691

UNCOV
692
        clone = deepcopy(self)
×
UNCOV
693
        clone.node = self.node.clone(memo)
×
UNCOV
694
        return clone
×
695

696
    def translate(self, vector, inplace=False, memo=None):
12✔
UNCOV
697
        if memo is None:
×
UNCOV
698
            memo = {}
×
UNCOV
699
        return type(self)(self.node.translate(vector, inplace, memo))
×
700

701
    def rotate(self, rotation, pivot=(0., 0., 0.), order='xyz', inplace=False,
12✔
702
               memo=None):
UNCOV
703
        if memo is None:
×
UNCOV
704
            memo = {}
×
UNCOV
705
        return type(self)(self.node.rotate(rotation, pivot=pivot, order=order,
×
706
                                           inplace=inplace, memo=memo))
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc