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

APN-Pucky / pyfeyn2 / 14318039396

07 Apr 2025 07:42PM UTC coverage: 67.018% (-1.3%) from 68.287%
14318039396

push

github

web-flow
Update gallery (#317)

* Add coment

* Add wikipedia like examples

Closes: #85

* Add feynmf examples

* Update gallery

* Fix serializer config indent

* No more feynml particle access

* dot2tex fixes

* clean

* add xcolor

* old dot2tex

* more fixes

* Fixed versions

* again

* again...

* uff

* Final

* more

* Timeout mermaid requests

* None on timeout

9 of 16 new or added lines in 4 files covered. (56.25%)

108 existing lines in 6 files now uncovered.

2160 of 3223 relevant lines covered (67.02%)

0.67 hits per line

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

70.86
/pyfeyn2/auto/position.py
1
import itertools
1✔
2
import logging
1✔
3
from itertools import permutations
1✔
4

5
import iminuit
1✔
6
import numpy as np
1✔
7
from feynml import Leg, Point, Propagator
1✔
8

9
from pyfeyn2.interface.dot import dot_to_positions, feynman_to_dot
1✔
10

11

12
# from https://stackoverflow.com/a/9997374
13
def ccw(A, B, C):
1✔
14
    """
15
    Return true if the points A, B, and C are in counter-clockwise order.
16
    """
17
    return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x)
1✔
18

19

20
# Return true if line segments AB and CD intersect
21
def intersect(A, B, C, D):
1✔
22
    """
23
    Return true if line segments AB and CD intersect
24

25
    Parameters
26
    ----------
27
    A : Point
28
        The first point of the first line segment.
29
    B : Point
30
        The second point of the first line segment.
31
    C : Point
32
        The first point of the second line segment.
33
    D : Point
34
        The second point of the second line segment.
35

36
    Returns
37
    -------
38
    bool
39
        True if the line segments intersect, False otherwise.
40

41
    Examples
42
    --------
43
    >>> A = Point(0, 0)
44
    >>> B = Point(1, 1)
45
    >>> C = Point(0, 1)
46
    >>> D = Point(1, 0)
47
    >>> intersect(A, B, C, D)
48
    True
49
    >>> A,B,C,D = Point(0,0), Point(1,1), Point(0,0), Point(1,0)
50
    >>> intersect(A, B, C, D)
51
    False
52
    """
53
    if A.x == C.x and A.y == C.y:
1✔
54
        return False
1✔
55
    if A.x == D.x and A.y == D.y:
1✔
56
        return False
1✔
57
    if B.x == C.x and B.y == C.y:
1✔
58
        return False
1✔
59
    if B.x == D.x and B.y == D.y:
1✔
60
        return False
1✔
61
    return ccw(A, C, D) != ccw(B, C, D) and ccw(A, B, C) != ccw(A, B, D)
1✔
62

63

64
def set_none_xy_to_zero(points):
1✔
65
    for p in points:
1✔
66
        if p.x is None:
1✔
67
            p.x = 0
1✔
68
        if p.y is None:
1✔
69
            p.y = 0
1✔
70

71

72
def require_xy(points):
1✔
73
    # check if a vertex or leg is missing a x or y position
74
    for v in points:
1✔
75
        if v.x is None:
1✔
76
            raise Exception(f"Vertex or leg {v} is missing x position.")
×
77
        if v.y is None:
1✔
78
            raise Exception(f"Vertex or leg {v} is missing y position.")
×
79

80

81
def _compute_number_of_intersects(fd):
1✔
82
    """
83
    Computes the number of crossed propagators/legs in a Feynman diagram
84
    """
85
    # check if a vertex or leg is missing a x or y position
86
    points = [*fd.vertices, *fd.legs]
1✔
87
    require_xy(points)
1✔
88
    lines = []
1✔
89
    for p in fd.propagators:
1✔
90
        src = fd.get_point(p.source)
1✔
91
        tar = fd.get_point(p.target)
1✔
92
        lines.append([src, tar])
1✔
93
    for l in fd.legs:
1✔
94
        if l.is_incoming():
1✔
95
            src = Point(l.x, l.y)
1✔
96
            tar = fd.get_point(l.target)
1✔
97
            lines.append([src, tar])
1✔
98
        elif l.is_outgoing():
1✔
99
            src = fd.get_point(l.target)
1✔
100
            tar = Point(l.x, l.y)
1✔
101
            lines.append([src, tar])
1✔
102

103
    ci = 0
1✔
104
    for i, l1 in enumerate(lines):
1✔
105
        for _, l2 in enumerate(lines[i + 1 :]):
1✔
106
            # test if the lines cross, without changing the lines
107
            if intersect(l1[0], l1[1], l2[0], l2[1]):
1✔
108
                ci += 1
1✔
109
    return ci
1✔
110

111

112
def auto_remove_intersections_by_permuting_legs(fd, adjust_points=False, size=10):
1✔
113
    """
114
    Automatically remove intersections by aligning the legs and reshufffling (permuting) them.
115
    """
116
    if adjust_points:
1✔
UNCOV
117
        fd = feynman_adjust_points(fd, size=size, clear_vertices=True)
×
118
    min_intersections = np.inf
1✔
119
    min_perm = 0
1✔
120
    inc = [l for l in fd.legs if l.is_incoming()]
1✔
121
    outc = [l for l in fd.legs if l.is_outgoing()]
1✔
122
    xyin = [[l.x, l.y] for l in inc]
1✔
123
    xyout = [[l.x, l.y] for l in outc]
1✔
124
    # loop over all permutations of incoming and outgoing legs
125
    for i, o in itertools.product(
1✔
126
        set(permutations(range(len(inc)))), set(permutations(range(len(outc))))
127
    ):
128
        for xyi, l in zip(xyin, i):
1✔
129
            inc[l].x = xyi[0]
1✔
130
            inc[l].y = xyi[1]
1✔
131
        for xyo, l in zip(xyout, o):
1✔
132
            outc[l].x = xyo[0]
1✔
133
            outc[l].y = xyo[1]
1✔
134
        if adjust_points:
1✔
UNCOV
135
            fd = feynman_adjust_points(fd, size=size, clear_vertices=True)
×
136
        ci = _compute_number_of_intersects(fd)
1✔
137
        # print(ci)
138
        logging.debug(f"auto_remove_intersections_by_align_legs: {ci=}")
1✔
139
        if ci < min_intersections:
1✔
140
            min_intersections = ci
1✔
141
            min_perm = (i, o)
1✔
142
            logging.debug(f"auto_remove_intersections_by_align_legs: {ci=}")
1✔
143
            logging.debug(f"auto_remove_intersections_by_align_legs: {i=} {o=}")
1✔
144
            logging.debug(f"auto_remove_intersections_by_align_legs: {xyin=} {xyout=}")
1✔
145
    # use/return best permutation
146
    for xyi, l in zip(xyin, min_perm[0]):
1✔
147
        inc[l].x = xyi[0]
1✔
148
        inc[l].y = xyi[1]
1✔
149
    for xyo, l in zip(xyout, min_perm[1]):
1✔
150
        outc[l].x = xyo[0]
1✔
151
        outc[l].y = xyo[1]
1✔
152
    if adjust_points:
1✔
UNCOV
153
        fd = feynman_adjust_points(fd, size=size, clear_vertices=True)
×
154
    return fd
1✔
155

156

157
def auto_remove_intersections_by_align_legs(fd, adjust_points=False, size=10):
1✔
158
    """
159
    Automatically remove intersections by aligning the legs and reshufffling (permuting) them.
160
    """
161
    fd = auto_align_legs(fd)
1✔
162
    return auto_remove_intersections_by_permuting_legs(
1✔
163
        fd, adjust_points=adjust_points, size=size
164
    )
165

166

167
def auto_align_legs(fd, incoming=None, outgoing=None):
1✔
168
    """
169
    Automatically reshuffle the legs of a Feynman diagram.
170
    """
171
    set_none_xy_to_zero(fd.legs)
1✔
172
    inc = [l for l in fd.legs if l.is_incoming()]
1✔
173
    outc = [l for l in fd.legs if l.is_outgoing()]
1✔
174
    if incoming is None or outgoing is None:
1✔
175
        f_min_x, f_min_y, f_max_x, f_max_y = fd.get_bounding_box()
1✔
176
        if incoming is None:
1✔
177
            incoming = [[f_min_x, y] for y in np.linspace(f_min_y, f_max_y, len(inc))]
1✔
178
        if outgoing is None:
1✔
179
            outgoing = [[f_max_x, y] for y in np.linspace(f_min_y, f_max_y, len(outc))]
1✔
180
    _auto_align(inc, incoming)
1✔
181
    _auto_align(outc, outgoing)
1✔
182
    return fd
1✔
183

184

185
def _get_dist(points, positions):
1✔
186
    dist = np.ones((len(points), len(positions))) * np.inf
1✔
187
    for i, v in enumerate(points):
1✔
188
        for j, p in enumerate(positions):
1✔
189
            dist[i][j] = np.sqrt((v.x - p[0]) ** 2 + (v.y - p[1]) ** 2)
1✔
190
    return dist
1✔
191

192

193
def _auto_align(points, positions):
1✔
194
    """
195
    Automatically position the vertices and legs on a list of positions.
196
    """
197
    logging.debug(f"_auto_align: positions {positions}")
1✔
198
    # check if a vertex or leg is missing a x or y position
199
    require_xy(points)
1✔
200
    # table of distances between vertices v and points p
201
    dist = _get_dist(points, positions)
1✔
202
    for i in range(len(points)):
1✔
203
        min_i, min_j = np.unravel_index(dist.argmin(), dist.shape)
1✔
204
        v = points[min_i]
1✔
205
        v.x = positions[min_j][0]
1✔
206
        v.y = positions[min_j][1]
1✔
207
        # remove min_i and min_j from dist
208
        dist[min_i, :] = np.inf
1✔
209
        dist[:, min_j] = np.inf
1✔
210

211

212
def auto_align(fd, positions, legs=True, vertices=True):
1✔
213
    """
214
    Automatically position the vertices and legs on a list of positions.
215

216
    Parameters
217
    ----------
218
    fd : FeynmanDiagram
219
        The Feynman diagram to be positioned.
220
    positions : list of tuple
221
        A list of tuples of the form (x,y) with the positions of the vertices
222
        and legs.
223
    legs : bool, optional
224
        Whether to position the legs, by default True
225
    vertices : bool, optional
226
        Whether to position the vertices, by default True
227

228
    Returns
229
    -------
230
    FeynmanDiagram
231
        The Feynman diagram with the vertices and legs positioned.
232
    """
233
    _auto_align(
1✔
234
        [*(fd.vertices if vertices else []), *(fd.legs if legs else [])], positions
235
    )
236
    return fd
1✔
237

238

239
def auto_grid(fd, n_x=None, n_y=None, min_x=None, min_y=None, max_x=None, max_y=None):
1✔
240
    """
241
    Automatically position the vertices and legs on a grid, with the given
242
    minimum and maximum values for x and y, and the number of grid points, but
243
    avoid placing vertices or legs on the same position.
244
    """
245
    # get the bounding box and construct grid from that
246
    positions = _get_grid(fd, n_x, n_y, min_x, min_y, max_x, max_y)
1✔
247
    return auto_align(fd, positions)
1✔
248

249

250
def _get_grid(fd, n_x=None, n_y=None, min_x=None, min_y=None, max_x=None, max_y=None):
1✔
251
    logging.debug(f"_get_grid {n_x}, {n_y}, {min_x}, {min_y}, {max_x}, {max_y}")
1✔
252
    f_min_x, f_min_y, f_max_x, f_max_y = fd.get_bounding_box()
1✔
253
    if n_x is None:
1✔
254
        n_x = len(fd.vertices) + len(fd.legs)
1✔
255
    if n_y is None:
1✔
256
        n_y = len(fd.vertices) + len(fd.legs)
1✔
257
    if min_x is None:
1✔
258
        min_x = f_min_x
1✔
259
    if max_x is None:
1✔
260
        max_x = f_max_x
1✔
261
    if min_y is None:
1✔
262
        min_y = f_min_y
1✔
263
    if max_y is None:
1✔
264
        max_y = f_max_y
1✔
265
    # print(min_x, max_x, min_y, max_y, n_x, n_y)
266
    xvalues = np.linspace(min_x, max_x, n_x)
1✔
267
    yvalues = np.linspace(min_y, max_y, n_y)
1✔
268
    xx, yy = np.meshgrid(xvalues, yvalues)
1✔
269
    positions = [[x, y] for x, y in zip(xx.flatten(), yy.flatten())]
1✔
270
    return positions
1✔
271

272

273
def auto_position(fd, layout="neato", size=5, clear_vertices=True):
1✔
274
    """Automatically position the vertices and legs."""
275
    # fd = scale_positions(fd, 10)
UNCOV
276
    fd = fd.with_style(f"layout : {layout}")
×
277
    fd = incoming_to_left(fd)
×
278
    fd = outgoing_to_right(fd)
×
279
    fd = feynman_adjust_points(fd, size=size, clear_vertices=clear_vertices)
×
280
    # fd = remove_unnecessary_vertices(fd)
281
    return fd
×
282

283

284
def incoming_to_left(fd):
1✔
285
    """Set the incoming legs to the left."""
286
    n = 0
×
UNCOV
287
    for l in fd.legs:
×
288
        if l.is_incoming():
×
UNCOV
289
            l.x = -2
×
UNCOV
290
            n = n + 1
×
UNCOV
291
    i = 0
×
UNCOV
292
    for l in fd.legs:
×
293
        if l.is_incoming():
×
294
            l.y = 4 / n * i
×
295
            i = i + 1
×
296

297
    return fd
×
298

299

300
def outgoing_to_right(fd):
1✔
301
    """Set the outgoing legs to the right."""
302
    n = 0
×
303
    for l in fd.legs:
×
UNCOV
304
        if not l.is_incoming():
×
UNCOV
305
            l.x = 2
×
UNCOV
306
            n = n + 1
×
UNCOV
307
    i = 0
×
308
    for l in fd.legs:
×
309
        if not l.is_incoming():
×
310
            l.y = 4 / n * i
×
311
            i = i + 1
×
312
    return fd
×
313

314

315
def scale_positions(fd, scale):
1✔
316
    """Scale the positions of the vertices and legs."""
317
    for v in fd.vertices:
×
318
        if v.x is not None:
×
UNCOV
319
            v.x *= scale
×
UNCOV
320
        if v.y is not None:
×
UNCOV
321
            v.y *= scale
×
UNCOV
322
    for l in fd.legs:
×
UNCOV
323
        if l.x is not None:
×
UNCOV
324
            l.x *= scale
×
UNCOV
325
        if l.y is not None:
×
UNCOV
326
            l.y *= scale
×
UNCOV
327
    return fd
×
328

329

330
def feynman_adjust_points(feyndiag, size=10, clear_vertices=False):
1✔
331
    """Adjust the points of the vertices and legs using Dot language algorithms."""
332
    fd = feyndiag
1✔
333
    if clear_vertices:
1✔
334
        for v in fd.vertices:
1✔
335
            v.x = None
1✔
336
            v.y = None
1✔
337
    norm = size
1✔
338
    dot = feynman_to_dot(fd, resubstituteslash=False)
1✔
339
    positions = dot_to_positions(dot)
1✔
340
    mmax = 0
1✔
341
    for _, p in positions.items():
1✔
342
        if p[0] > mmax:
1✔
343
            mmax = p[0]
1✔
344
        if p[1] > mmax:
1✔
345
            mmax = p[1]
1✔
346
    for v in fd.vertices:
1✔
347
        if v.id in positions:
1✔
348
            v.x = positions[v.id][0] / mmax * norm
1✔
349
            v.y = positions[v.id][1] / mmax * norm
1✔
350
    for l in fd.legs:
1✔
351
        l.x = positions[l.id][0] / mmax * norm
1✔
352
        l.y = positions[l.id][1] / mmax * norm
1✔
353
    return fd
1✔
354

355

356
def remove_unnecessary_vertices(feyndiag):
1✔
357
    """Remove vertices that are only connected to two vertices with the same propagator."""
UNCOV
358
    fd = feyndiag
×
359
    vertices = []
×
360
    for v in fd.vertices:
×
361
        ps = fd.get_connections(v)
×
362
        if (
×
363
            len(ps) == 2
364
            and ps[0].pdgid == ps[1].pdgid
365
            and isinstance(ps[0], Propagator)
366
            and isinstance(ps[1], Propagator)
367
        ):
UNCOV
368
            if ps[0].source == v.id and ps[1].target == v.id:
×
369
                ps[0].source = ps[1].source
×
370
                fd.remove_propagator(ps[1])
×
371
            elif ps[0].target == v.id and ps[1].source == v.id:
×
372
                ps[1].source = ps[0].source
×
UNCOV
373
                fd.remove_propagator(ps[0])
×
374
            else:
UNCOV
375
                raise Exception(
×
376
                    f"Unknown case, source == source or target == target, {v} {ps[0]} {ps[1]}"
377
                )
UNCOV
378
            continue
×
UNCOV
379
        vertices.append(v)
×
UNCOV
380
    fd.vertices = vertices
×
UNCOV
381
    return fd
×
382

383

384
def quad(points, cons, all_points, *args, dis=1.0):
1✔
385
    for i, p in enumerate(points):
1✔
386
        p.x = args[2 * i]
1✔
387
        p.y = args[2 * i + 1]
1✔
388
    r = dis
1✔
389
    LenJ = 0
1✔
390
    if dis != 0.0:
1✔
391
        for i, p in enumerate(points):
1✔
392
            for j in cons[i]:
1✔
393
                if all_points[j].x is not None and all_points[j].y is not None:
1✔
394
                    LenJ = LenJ + (
1✔
395
                        (
396
                            (
397
                                (
398
                                    (p.x - all_points[j].x) ** 2
399
                                    + (p.y - all_points[j].y) ** 2
400
                                )
401
                                ** 0.5
402
                                - r
403
                            )
404
                        )
405
                        ** 2
406
                    )
407
    return LenJ
1✔
408

409

410
def lennard_jones(points, cons, all_points, *args, LJ=1.0):
1✔
411
    for i, p in enumerate(points):
1✔
412
        p.x = args[2 * i]
1✔
413
        p.y = args[2 * i + 1]
1✔
414
    r = LJ
1✔
415
    LenJ = 0
1✔
416
    if LJ != 0.0:
1✔
UNCOV
417
        for i, p in enumerate(points):
×
UNCOV
418
            for j in cons[i]:
×
UNCOV
419
                if all_points[j].x is not None and all_points[j].y is not None:
×
UNCOV
420
                    LenJ = (
×
421
                        LenJ
422
                        - (
423
                            (
424
                                (
425
                                    (
426
                                        (p.x - all_points[j].x) ** 2
427
                                        + (p.y - all_points[j].y) ** 2
428
                                    )
429
                                    ** 0.5
430
                                )
431
                                / r
432
                            )
433
                            ** 6
434
                        )
435
                        + (
436
                            (
437
                                (
438
                                    (p.x - all_points[j].x) ** 2
439
                                    + (p.y - all_points[j].y) ** 2
440
                                )
441
                                ** 0.5
442
                            )
443
                            / r
444
                        )
445
                        ** 12
446
                    )
447
    return LenJ
1✔
448

449

450
def auto_vdw(
1✔
451
    fd, points=None, LJ=0.0, dis=None, y_symmetry=0.0, x_symmetry=0.0, intersection=0.0
452
):
453
    """
454
    Minimizes a potential between vertices and legs.
455
    Further the function to be minimized gets punished by the number of intersections scaled by intersection.
456
    The function to be minimized gets punished by the asymmetry in x and y direction scaled by x_symmetry and y_symmetry.
457

458
    Parameters
459
    ----------
460
    fd : FeynmanDiagram
461
        The Feynman diagram to be positioned.
462
    points : list of Point, optional
463
        The points (leg or vertex) to be positioned. Recommended values are fd.vertices or [*fd.vertices, *fd.legs]
464
    LJ : float, optional
465
        The strength of the Lennard-Jones potential, by default 1.0
466
    y_symmetry : float, optional
467
        The strength of the punishment for asymmetry in y direction, by default 0.0
468
    x_symmetry : float, optional
469
        The strength of the punishment for asymmetry in x direction, by default 0.0
470
    intersection : float, optional
471
        The strength of the punishment for intersections, by default 0.0
472

473
    Returns
474
    -------
475
    FeynmanDiagram
476
        The Feynman diagram with the vertices and legs positioned.
477
    """
478
    if points is None:
1✔
479
        points = fd.vertices
1✔
480
    if dis is None:
1✔
481
        # set dis to number of points
482
        dis = 4.0 / (len(points) / 2) ** 0.5
1✔
483

484
    all_points = [*fd.vertices, *fd.legs]
1✔
485
    set_none_xy_to_zero(points)
1✔
486
    # get distance to connected points
487
    cons = []
1✔
488
    # dist = []
489
    for p in points:
1✔
490
        n = []
1✔
491
        # dd = []
492
        for c in fd.get_neighbours(p):
1✔
493
            for j, pp in enumerate(all_points):
1✔
494
                if pp.x is None or pp.y is None:
1✔
UNCOV
495
                    continue
×
496
                if pp.id == c.id:
1✔
497
                    n.append(j)
1✔
498
                    # dd.append(np.sqrt((p.x - pp.x) ** 2 + (p.y - pp.y) ** 2))
499
        cons.append(n)
1✔
500
        # dist.append(dd)
501

502
    def fun(*args):
1✔
503
        for i, p in enumerate(points):
1✔
504
            p.x = args[2 * i]
1✔
505
            p.y = args[2 * i + 1]
1✔
506
        LenJ = lennard_jones(points, cons, all_points, *args, LJ=LJ)
1✔
507
        qdis = quad(points, cons, all_points, *args, dis=dis)
1✔
508
        inter = 0
1✔
509
        if intersection != 0.0:
1✔
510
            inter += intersection * _compute_number_of_intersects(fd)
×
511
        pun_x = 0
1✔
512
        if x_symmetry != 0.0:
1✔
513
            min_x, min_y, max_x, max_y = fd.get_bounding_box()
×
514
            # get averate x and y
515
            avg_x = (min_x + max_x) / 2
×
516
            avg_y = (min_y + max_y) / 2
×
517
            pun = 0
×
518
            for p in points:
×
519
                nx = p.x - 2 * (p.x - avg_x)
×
520
                # find nearest point to (nx, p.y)
UNCOV
521
                min_dist = np.inf
×
UNCOV
522
                for pp in all_points:
×
523
                    if pp.id == p.id or pp.x is None or pp.y is None:
×
UNCOV
524
                        continue
×
525
                    dist = np.sqrt((nx - pp.x) ** 2 + (p.y - pp.y) ** 2)
×
526
                    if dist < min_dist:
×
527
                        min_dist = dist
×
528
                pun += min_dist
×
529
            pun_x = pun
×
530
        pun_y = 0
1✔
531
        if y_symmetry != 0.0:
1✔
532
            min_x, min_y, max_x, max_y = fd.get_bounding_box()
×
533
            # get averate x and y
534
            avg_x = (min_x + max_x) / 2
×
535
            avg_y = (min_y + max_y) / 2
×
536
            pun = 0
×
537
            for p in points:
×
538
                ny = p.y - 2 * (p.y - avg_y)
×
539
                # find nearest point to (p.x, ny)
UNCOV
540
                min_dist = np.inf
×
UNCOV
541
                for pp in all_points:
×
UNCOV
542
                    if pp.id == p.id or pp.x is None or pp.y is None:
×
UNCOV
543
                        continue
×
UNCOV
544
                    dist = np.sqrt((p.x - pp.x) ** 2 + (ny - pp.y) ** 2)
×
UNCOV
545
                    if dist < min_dist:
×
UNCOV
546
                        min_dist = dist
×
UNCOV
547
                pun += min_dist
×
UNCOV
548
            pun_y = pun
×
549
        return qdis + LenJ + inter + pun_x * x_symmetry + pun_y * y_symmetry
1✔
550

551
    m = iminuit.Minuit(fun, *[0 for _ in range(len(points) * 2)])
1✔
552
    v = m.migrad()
1✔
553
    args = list(v.values.to_dict().values())
1✔
554
    for i, p in enumerate(points):
1✔
555
        p.x = args[2 * i]
1✔
556
        p.y = args[2 * i + 1]
1✔
557
    return fd
1✔
558

559

560
def auto_gridded_springs(
1✔
561
    fd,
562
    points=None,
563
    n_x=None,
564
    n_y=None,
565
    min_x=None,
566
    min_y=None,
567
    max_x=None,
568
    max_y=None,
569
    **kwargs,
570
):  # TODO replace kwargs by actual arguments (i.e. for documentation)
571
    fd = auto_vdw(fd, points, **kwargs)
1✔
572
    fd = auto_grid(
1✔
573
        fd, n_x=n_x, n_y=n_y, min_x=min_x, min_y=min_y, max_x=max_x, max_y=max_y
574
    )
575
    return fd
1✔
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

© 2026 Coveralls, Inc