1######################################################################
2#                                                                    #
3#  Copyright 2009 Lucas Heitzmann Gabrielli.                         #
4#  This file is part of gdspy, distributed under the terms of the    #
5#  Boost Software License - Version 1.0.  See the accompanying       #
6#  LICENSE file or <http://www.boost.org/LICENSE_1_0.txt>            #
7#                                                                    #
8######################################################################
9
10import pytest
11import gdspy
12import uuid
13import datetime
14from tutils import hash_file
15
16gdspy.library.use_current_library = False
17
18
19def unique():
20    return str(uuid.uuid4())
21
22
23def test_add():
24    lib = gdspy.GdsLibrary()
25    c1 = gdspy.Cell("gl_add_1")
26    c2 = gdspy.Cell("gl_add_2")
27    c3 = gdspy.Cell("gl_add_3")
28    r1 = gdspy.CellReference(c1)
29    with pytest.warns(UserWarning):
30        r2 = gdspy.CellReference("gl_add_2")
31    c3.add([r1, r2])
32    lib.add(c1)
33    lib.add((c2, c3))
34    assert lib.cells == {"gl_add_1": c1, "gl_add_2": c2, "gl_add_3": c3}
35    lib = gdspy.GdsLibrary()
36    lib.add(c3)
37    assert lib.cells == {"gl_add_1": c1, "gl_add_3": c3}
38
39
40def test_duplicate():
41    name = "gl_duplicate"
42    lib = gdspy.GdsLibrary()
43    lib.add(gdspy.Cell(name))
44    c = gdspy.Cell(name)
45    with pytest.raises(ValueError) as e:
46        lib.add(c)
47    assert name in str(e.value)
48
49    lib.add(c, overwrite_duplicate=True)
50    assert lib.cells == {name: c}
51
52    cl = [gdspy.Cell(name), gdspy.Cell(name + "1")]
53    with pytest.raises(ValueError) as e:
54        lib.add(cl)
55    assert name in str(e.value)
56
57    lib.add(cl, overwrite_duplicate=True)
58    assert lib.cells == {name: cl[0], name + "1": cl[1]}
59
60
61def test_add_update():
62    lib = gdspy.GdsLibrary()
63    main = gdspy.Cell("MAIN")
64    c1 = gdspy.Cell("C1")
65    c2 = gdspy.Cell("C2")
66    c3 = gdspy.Cell("C1")
67    r1 = gdspy.CellReference(c1)
68    main.add(r1)
69    with pytest.warns(UserWarning):
70        r2 = gdspy.CellArray("C1", 1, 1, (1, 1))
71    main.add(r2)
72    r3 = gdspy.CellReference(c2)
73    main.add(r3)
74    r4 = gdspy.CellReference(c3)
75    c2.add(r4)
76    with pytest.warns(UserWarning):
77        r5 = gdspy.CellReference("C3")
78    c1.add(r5)
79    with pytest.warns(UserWarning):
80        r6 = gdspy.CellReference("C2")
81    main.add(r6)
82    lib.add([main, c1, c2], include_dependencies=False)
83    lib.add(c3, include_dependencies=False, overwrite_duplicate=True)
84    assert r1.ref_cell is c3
85    assert r2.ref_cell is c3
86    assert r3.ref_cell is c2
87    assert r4.ref_cell is c3
88    assert r5.ref_cell == "C3"
89    assert r6.ref_cell == "C2"
90
91
92def test_add_update2():
93    lib = gdspy.GdsLibrary()
94    main = gdspy.Cell("MAIN")
95    c1 = gdspy.Cell("C1")
96    c2 = gdspy.Cell("C2")
97    c3 = gdspy.Cell("C1")
98    r1 = gdspy.CellReference(c1)
99    main.add(r1)
100    with pytest.warns(UserWarning):
101        r2 = gdspy.CellArray("C1", 1, 1, (1, 1))
102    main.add(r2)
103    r3 = gdspy.CellReference(c2)
104    main.add(r3)
105    r4 = gdspy.CellReference(c3)
106    c2.add(r4)
107    with pytest.warns(UserWarning):
108        r5 = gdspy.CellReference("C3")
109    c1.add(r5)
110    with pytest.warns(UserWarning):
111        r6 = gdspy.CellReference("C2")
112    main.add(r6)
113    lib.add([main, c1, c2], include_dependencies=False)
114    lib.add(
115        c3,
116        include_dependencies=False,
117        overwrite_duplicate=True,
118        update_references=False,
119    )
120    assert r1.ref_cell is c1
121    assert r2.ref_cell == "C1"
122    assert r3.ref_cell is c2
123    assert r4.ref_cell is c3
124    assert r5.ref_cell == "C3"
125    assert r6.ref_cell == "C2"
126
127
128def test_remove():
129    lib = gdspy.GdsLibrary()
130    main = gdspy.Cell("MAIN")
131    c1 = gdspy.Cell("C1")
132    c2 = gdspy.Cell("C2")
133    c3 = gdspy.Cell("C1")
134    r1 = gdspy.CellReference(c1)
135    main.add(r1)
136    with pytest.warns(UserWarning):
137        r2 = gdspy.CellArray("C1", 1, 1, (1, 1))
138    main.add(r2)
139    r3 = gdspy.CellReference(c2)
140    main.add(r3)
141    r4 = gdspy.CellReference(c3)
142    c2.add(r4)
143    with pytest.warns(UserWarning):
144        r5 = gdspy.CellReference("C3")
145    c1.add(r5)
146    with pytest.warns(UserWarning):
147        r6 = gdspy.CellReference("C2")
148    main.add(r6)
149    lib.add([main, c1, c2], include_dependencies=False)
150    assert lib.remove("C3") == 1
151    assert len(c1.references) == 0
152    assert len(c2.references) == 1
153    assert c2.references[0] is r4
154    assert lib.remove(c1) == 3
155    assert "C1" not in lib.cells
156    assert len(main.references) == 2
157    assert main.references[0] is r3
158    assert main.references[1] is r6
159    assert len(c2.references) == 0
160
161
162def test_replace():
163    lib = gdspy.GdsLibrary()
164    main = gdspy.Cell("MAIN")
165    c1 = gdspy.Cell("C1")
166    c2 = gdspy.Cell("C2")
167    c3 = gdspy.Cell("C1")
168    r1 = gdspy.CellReference(c1)
169    main.add(r1)
170    with pytest.warns(UserWarning):
171        r2 = gdspy.CellArray("C1", 1, 1, (1, 1))
172    main.add(r2)
173    r3 = gdspy.CellReference(c2)
174    main.add(r3)
175    r4 = gdspy.CellReference(c3)
176    c2.add(r4)
177    with pytest.warns(UserWarning):
178        r5 = gdspy.CellReference("C3")
179    c1.add(r5)
180    with pytest.warns(UserWarning):
181        r6 = gdspy.CellReference("C2")
182    main.add(r6)
183    lib.add([main, c1, c2], include_dependencies=False)
184    assert lib.replace_references(c2, c3) == 2
185    assert r3.ref_cell is c3
186    assert r6.ref_cell is c3
187    assert lib.replace_references("C3", "C1") == 1
188    assert r5.ref_cell is c1
189    assert lib.replace_references("C1", c2) == 6
190    assert r1.ref_cell is c2
191    assert r2.ref_cell is c2
192    assert r3.ref_cell is c2
193    assert r4.ref_cell is c2
194    assert r5.ref_cell is c2
195    assert r6.ref_cell is c2
196
197
198def test_rename():
199    lib = gdspy.GdsLibrary()
200    main = gdspy.Cell("MAIN")
201    c1 = gdspy.Cell("C1")
202    c2 = gdspy.Cell("C2")
203    c3 = gdspy.Cell("C1")
204    r1 = gdspy.CellReference(c1)
205    main.add(r1)
206    with pytest.warns(UserWarning):
207        r2 = gdspy.CellArray("C1", 1, 1, (1, 1))
208    main.add(r2)
209    r3 = gdspy.CellReference(c2)
210    main.add(r3)
211    r4 = gdspy.CellReference(c3)
212    c2.add(r4)
213    with pytest.warns(UserWarning):
214        r5 = gdspy.CellReference("C3")
215    c1.add(r5)
216    with pytest.warns(UserWarning):
217        r6 = gdspy.CellReference("C2")
218    main.add(r6)
219    lib.add([main, c1, c2], include_dependencies=False)
220    with pytest.raises(ValueError):
221        lib.rename_cell(c3, "C3")
222    assert c3.name == "C1"
223    with pytest.raises(ValueError):
224        lib.rename_cell(c2, "C1")
225    assert c2.name == "C2"
226    assert lib.rename_cell(c1, "C3") == 2
227    assert c1.name == "C3"
228    assert lib.cells["C3"] is c1
229    assert lib.rename_cell(c2, "X2", False) == 0
230    assert c2.name == "X2"
231    assert lib.cells["X2"] is c2
232    assert r1.ref_cell is c1
233    assert r2.ref_cell is c1
234    assert r3.ref_cell is c2
235    assert r4.ref_cell is c1
236    assert r5.ref_cell == "C3"
237    assert r6.ref_cell == "C2"
238
239
240@pytest.fixture
241def tree():
242    c = [gdspy.Cell("tree_" + unique()) for _ in range(8)]
243    lib = gdspy.GdsLibrary()
244    lib.add(c)
245    c[0].add(gdspy.CellReference(c[1]))
246    c[0].add(gdspy.CellReference(c[3]))
247    c[1].add(gdspy.CellReference(c[2]))
248    c[1].add(gdspy.CellArray(c[2], 2, 1, (0, 0)))
249    c[1].add(gdspy.CellArray(c[3], 2, 1, (0, 0)))
250    c[4].add(gdspy.CellReference(c[3]))
251    c[6].add(gdspy.CellArray(c[5], 2, 1, (0, 0)))
252    return lib, c
253
254
255def test_top_level_1(tree):
256    lib, c = tree
257    tl = lib.top_level()
258    assert len(tl) == 4 and c[0] in tl and c[4] in tl and c[6] in tl and c[7] in tl
259
260
261def test_top_level_2(tree):
262    lib, c = tree
263    c[7].add(gdspy.CellReference(c[0]))
264    c[7].add(gdspy.CellReference(c[4]))
265    c[7].add(gdspy.CellReference(c[6]))
266    assert lib.top_level() == [c[7]]
267
268
269def test_top_level_3(tree):
270    lib, c = tree
271    c[7].add(gdspy.CellReference(c[0]))
272    c[3].add(gdspy.CellReference(c[4]))
273    c[2].add(gdspy.CellReference(c[6]))
274    c[1].add(gdspy.CellReference(c[7]))
275    assert lib.top_level() == []
276
277
278def test_rw_gds(tmpdir):
279    lib = gdspy.GdsLibrary("lib", unit=2e-3, precision=1e-5)
280    c1 = gdspy.Cell("gl_rw_gds_1")
281    c1.add(gdspy.Rectangle((0, -1), (1, 2), 2, 4))
282    c1.add(gdspy.Label("label", (1, -1), "w", 45, 1.5, True, 5, 6))
283    c2 = gdspy.Cell("gl_rw_gds_2")
284    c2.add(gdspy.Round((0, 0), 1, number_of_points=32, max_points=20))
285    c3 = gdspy.Cell("gl_rw_gds_3")
286    c3.add(gdspy.CellReference(c1, (0, 1), -90, 2, True))
287    c4 = gdspy.Cell("gl_rw_gds_4")
288    c4.add(gdspy.CellArray(c2, 2, 3, (1, 4), (-1, -2), 180, 0.5, True))
289    lib.add((c1, c2, c3, c4))
290
291    fname1 = str(tmpdir.join("test1.gds"))
292    lib.write_gds(fname1)
293    lib1 = gdspy.GdsLibrary(
294        infile=fname1,
295        unit=1e-3,
296        precision=1e-6,
297        units="convert",
298        rename={"gl_rw_gds_1": "1"},
299        layers={2: 4},
300        datatypes={4: 2},
301        texttypes={6: 7},
302    )
303    assert lib1.name == "lib"
304    assert len(lib1.cells) == 4
305    assert set(lib1.cells.keys()) == {"1", "gl_rw_gds_2", "gl_rw_gds_3", "gl_rw_gds_4"}
306    c = lib1.cells["1"]
307    assert len(c.polygons) == len(c.labels) == 1
308    assert c.polygons[0].area() == 12.0
309    assert c.polygons[0].layers == [4]
310    assert c.polygons[0].datatypes == [2]
311    assert c.labels[0].text == "label"
312    assert c.labels[0].position[0] == 2 and c.labels[0].position[1] == -2
313    assert c.labels[0].anchor == 4
314    assert c.labels[0].rotation == 45
315    assert c.labels[0].magnification == 1.5
316    assert c.labels[0].x_reflection == True
317    assert c.labels[0].layer == 5
318    assert c.labels[0].texttype == 7
319
320    c = lib1.cells["gl_rw_gds_2"]
321    assert len(c.polygons) == 2
322    assert isinstance(c.polygons[0], gdspy.Polygon) and isinstance(
323        c.polygons[1], gdspy.Polygon
324    )
325
326    c = lib1.cells["gl_rw_gds_3"]
327    assert len(c.references) == 1
328    assert isinstance(c.references[0], gdspy.CellReference)
329    assert c.references[0].ref_cell == lib1.cells["1"]
330    assert c.references[0].origin[0] == 0 and c.references[0].origin[1] == 2
331    assert c.references[0].rotation == -90
332    assert c.references[0].magnification == 2
333    assert c.references[0].x_reflection == True
334
335    c = lib1.cells["gl_rw_gds_4"]
336    assert len(c.references) == 1
337    assert isinstance(c.references[0], gdspy.CellArray)
338    assert c.references[0].ref_cell == lib1.cells["gl_rw_gds_2"]
339    assert c.references[0].origin[0] == -2 and c.references[0].origin[1] == -4
340    assert c.references[0].rotation == 180
341    assert c.references[0].magnification == 0.5
342    assert c.references[0].x_reflection == True
343    assert c.references[0].spacing[0] == 2 and c.references[0].spacing[1] == 8
344    assert c.references[0].columns == 2
345    assert c.references[0].rows == 3
346
347    fname2 = str(tmpdir.join("test2.gds"))
348    lib.name = "lib2"
349    with open(fname2, "wb") as fout:
350        lib.write_gds(fout)
351    with open(fname2, "rb") as fin:
352        lib2 = gdspy.GdsLibrary()
353        lib2.read_gds(fin)
354    assert lib2.name == "lib2"
355    assert len(lib2.cells) == 4
356
357
358def test_properties(tmpdir):
359    lib = gdspy.GdsLibrary()
360    c1 = lib.new_cell("POLY")
361    rect = gdspy.Rectangle((0, 0), (2, 1))
362    rect.properties[1] = "test1"
363    rect.properties[126] = "test_126"
364    lbl = gdspy.Label("LABEL", (20, 20))
365    lbl.properties[2] = "test2"
366    lbl.properties[22] = "test22"
367    c1.add((rect, lbl))
368    c2 = lib.new_cell("REF")
369    ref = gdspy.CellReference(c1, (0, 0))
370    ref.properties[6] = "test6"
371    ref.properties[121] = "test_121"
372    c2.add(ref)
373    c3 = lib.new_cell("AREF")
374    aref = gdspy.CellArray(c1, 1, 2, (30, 40), (0, 0))
375    aref.properties[4] = "test4"
376    aref.properties[123] = "test_123"
377    c3.add(aref)
378    c4 = lib.new_cell("FP")
379    fp = gdspy.FlexPath([(0, 0), (0, 1), (1, 1)], 0.01, gdsii_path=True)
380    fp.properties[2] = "test2"
381    fp.properties[125] = "test_125"
382    c4.add(fp)
383    c5 = lib.new_cell("RP")
384    rp = gdspy.RobustPath((0, 0), [0.01, 0.01], 0.5, gdsii_path=True).segment((1, 1))
385    rp.properties[3] = "test3"
386    rp.properties[124] = "test_124"
387    c5.add(rp)
388    fname = str(tmpdir.join("test_properties.gds"))
389    lib.write_gds(fname)
390    lib1 = gdspy.GdsLibrary(infile=fname)
391    assert rect.properties == lib1.cells["POLY"].polygons[0].properties
392    assert lbl.properties == lib1.cells["POLY"].labels[0].properties
393    assert ref.properties == lib1.cells["REF"].references[0].properties
394    assert aref.properties == lib1.cells["AREF"].references[0].properties
395    assert fp.properties == lib1.cells["FP"].paths[0].properties
396    assert rp.properties == lib1.cells["RP"].paths[0].properties
397    assert rp.properties == lib1.cells["RP"].paths[1].properties
398
399
400def test_time_changes_gds_hash(tmpdir):
401    fn1 = str(tmpdir.join('nofreeze1.gds'))
402    fn2 = str(tmpdir.join('nofreeze2.gds'))
403    date1 = datetime.datetime(1988, 8, 28)
404    date2 = datetime.datetime(2020, 12, 25)
405    lib = gdspy.GdsLibrary(name='speedy')
406    lib.new_cell("empty")
407    lib.write_gds(fn1, timestamp=date1)
408    hash1 = hash_file(fn1)
409    lib.write_gds(fn2, timestamp=date2)
410    hash2 = hash_file(fn2)
411
412    assert hash1 != hash2
413
414
415def test_frozen_gds_has_constant_hash(tmpdir):
416    fn1 = str(tmpdir.join('freeze1.gds'))
417    fn2 = str(tmpdir.join('freeze2.gds'))
418    frozen_date = datetime.datetime(1988, 8, 28)
419    lib = gdspy.GdsLibrary(name='Elsa')
420    lib.new_cell("empty")
421    lib.write_gds(fn1, timestamp=frozen_date)
422    hash1 = hash_file(fn1)
423    lib.write_gds(fn2, timestamp=frozen_date)
424    hash2 = hash_file(fn2)
425
426    assert hash1 == hash2
427
428
429def test_frozen_gds_with_cell_has_constant_hash(tmpdir):
430    fn1 = str(tmpdir.join('freezec1.gds'))
431    fn2 = str(tmpdir.join('freezec2.gds'))
432    frozen_date = datetime.datetime(1988, 8, 28)
433    lib = gdspy.GdsLibrary(name='Elsa')
434    cell = gdspy.Cell(name='Anna')
435    cell.add(gdspy.Rectangle((0, 0), (100, 1000)))
436    lib.add(cell)
437    lib.write_gds(fn1, timestamp=frozen_date)
438    hash1 = hash_file(fn1)
439    lib.write_gds(fn2, timestamp=frozen_date)
440    hash2 = hash_file(fn2)
441
442    assert hash1 == hash2
443
444
445def test_frozen_gds_with_cell_array_has_constant_hash(tmpdir):
446    fn1 = str(tmpdir.join('freezea1.gds'))
447    fn2 = str(tmpdir.join('freezea2.gds'))
448    frozen_date = datetime.datetime(1988, 8, 28)
449    lib = gdspy.GdsLibrary(name='Elsa')
450    cell = gdspy.Cell(name='Anna')
451    cell.add(gdspy.Rectangle((0, 0), (100, 1000)))
452    cell2 = gdspy.Cell(name='Olaf')
453    cell2.add(gdspy.Rectangle((0, 0), (50, 100)))
454
455    cell_array = gdspy.CellArray(ref_cell=cell2, columns=5, rows=2, spacing=(60, 120), origin=(1000, 0))
456    cell.add(cell_array)
457    lib.add(cell)
458    lib.write_gds(fn1, timestamp=frozen_date)
459    hash1 = hash_file(fn1)
460    lib.write_gds(fn2, timestamp=frozen_date)
461    hash2 = hash_file(fn2)
462
463    assert hash1 == hash2
464