Skip to content

mnns.nanowires

Functions to modify nanowire networks.

Functions:

Name Description
add_electrodes

Convenience function for adding electrodes on the edges of a network

add_wires

Adds wires to a given nanowire network in-place. Returns the nodes of the

convert_NWN_to_MNR

Converts a JDA NWN to an MNR NWN in-place.

get_edge_indices

Given a NWN and a list of edges, returns two lists: one of the indices of

add_electrodes

add_electrodes(
    NWN: NanowireNetwork, *args
) -> list[JDANode]

Convenience function for adding electrodes on the edges of a network in-place. Returns the nodes of the added electrodes in order.

Can be called in two ways:

add_electrodes(NWN, *str)
    where *str are strings with values {"left", "right", "top, "bottom"}

add_electrodes(NWN, *iterable)
    where *iterable are iterables where first entry is a string (as
    before) the second entry is number of electrodes on that side,
    and the third entry is the spacing between the electrodes,
    assumed to be in units of l0. An optional fourth entry can be
    given which is a list of offsets: a float value for each electrode.

Parameters:

Name Type Description Default
NWN Graph

Nanowire network.

required
*args

See function description.

()

Returns:

Name Type Description
new_wire_nodes list of tuples

List of the newly added nodes. If strings were passed, the list follows the order passed. If iterables were passed, the list is ordered from left-to-right or bottom-to-top concatenated.

Source code in mnns/nanowires.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
def add_electrodes(NWN: NanowireNetwork, *args) -> list[JDANode]:
    """
    Convenience function for adding electrodes on the edges of a network
    in-place. Returns the nodes of the added electrodes in order.

    Can be called in two ways:

        add_electrodes(NWN, *str)
            where *str are strings with values {"left", "right", "top, "bottom"}

        add_electrodes(NWN, *iterable)
            where *iterable are iterables where first entry is a string (as
            before) the second entry is number of electrodes on that side,
            and the third entry is the spacing between the electrodes,
            assumed to be in units of l0. An optional fourth entry can be
            given which is a list of offsets: a float value for each electrode.

    Parameters
    ----------
    NWN : Graph
        Nanowire network.

    *args
        See function description.

    Returns
    -------
    new_wire_nodes : list of tuples
        List of the newly added nodes. If strings were passed, the list
        follows the order passed. If iterables were passed, the list is
        ordered from left-to-right or bottom-to-top concatenated.

    """
    length = NWN.graph["length"]
    width = NWN.graph["width"]
    line_list = []
    seen = []

    # Method one
    if all(isinstance(arg, str) for arg in args):
        for side in args:
            if side in seen:
                raise ValueError(f"Duplicate side: {side}")
            elif side == "left":
                line_list.append(LineString([(0, 0), (0, width)]))
            elif side == "right":
                line_list.append(LineString([(length, 0), (length, width)]))
            elif side == "top":
                line_list.append(LineString([(0, width), (length, width)]))
            elif side == "bottom":
                line_list.append(LineString([(0, 0), (length, 0)]))
            else:
                raise ValueError(f"Invalid side: {side}")
            seen.append(side)

    # Method two
    elif all(isinstance(arg, Iterable) for arg in args):
        for itr in args:
            # Unpack list
            if len(itr) == 3:
                side, num, spacing = itr
                offsets = [0.0] * num
            elif len(itr) == 4:
                side, num, spacing, offsets = itr
            else:
                raise ValueError("Invalid arguments.")

            # Cannot have the same side twice
            if side in seen:
                raise ValueError(f"Duplicate side: {side}")

            # Add electrodes
            elif side == "left":
                for i in range(num):
                    delta = offsets[i]
                    start = (i / num * width) + (spacing / 2)
                    end = ((i + 1) / num * width) - (spacing / 2)
                    line_list.append(
                        LineString([(0, start + delta), (0, end + delta)])
                    )
            elif side == "right":
                for i in range(num):
                    delta = offsets[i]
                    start = (i / num * width) + (spacing / 2)
                    end = ((i + 1) / num * width) - (spacing / 2)
                    line_list.append(
                        LineString(
                            [(length, start + delta), (length, end + delta)]
                        )
                    )
            elif side == "top":
                for i in range(num):
                    delta = offsets[i]
                    start = (i / num * length) + (spacing / 2)
                    end = ((i + 1) / num * length) - (spacing / 2)
                    line_list.append(
                        LineString(
                            [(start + delta, width), (end + delta, width)]
                        )
                    )
            elif side == "bottom":
                for i in range(num):
                    delta = offsets[i]
                    start = (i / num * length) + (spacing / 2)
                    end = ((i + 1) / num * length) - (spacing / 2)
                    line_list.append(
                        LineString([(start + delta, 0), (end + delta, 0)])
                    )
            else:
                raise ValueError(f"Invalid side: {side}")

    else:
        raise ValueError(
            "Arguments after NWN must be all strings or all lists."
        )

    # Add wires to the network
    new_wire_nodes = add_wires(NWN, line_list, [True] * len(line_list))
    return new_wire_nodes

add_wires

add_wires(
    NWN: NanowireNetwork,
    lines: list[LineString],
    electrodes: list[bool],
    resistance: float = None,
) -> list[JDANode]

Adds wires to a given nanowire network in-place. Returns the nodes of the added wires in order.

Currently, adding a wire that already exists breaks things.

Also, adding wires to a MNR NWN does not work yet.

Parameters:

Name Type Description Default
NWN Graph

Nanowire network to add wires to.

required
lines list of LineStrings

A list of Shapely LineStrings, representing nanowires, to be added.

required
electrodes list of bool

A list of boolean values specifying whether or not the corresponding nanowire in lines is an electrode.

required
resistance float

Junction resistances of the added wires.

None

Returns:

Name Type Description
new_wire_nodes list of tuples

List of the newly added nodes in the same order in lines.

Source code in mnns/nanowires.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def add_wires(
    NWN: NanowireNetwork,
    lines: list[LineString],
    electrodes: list[bool],
    resistance: float = None,
) -> list[JDANode]:
    """
    Adds wires to a given nanowire network in-place. Returns the nodes of the
    added wires in order.

    Currently, adding a wire that already exists breaks things.

    Also, adding wires to a MNR NWN does not work yet.

    Parameters
    ----------
    NWN : Graph
        Nanowire network to add wires to.

    lines : list of LineStrings
        A list of Shapely LineStrings, representing nanowires, to be added.

    electrodes : list of bool
        A list of boolean values specifying whether or not the corresponding
        nanowire in `lines` is an electrode.

    resistance : float, optional
        Junction resistances of the added wires.

    Returns
    -------
    new_wire_nodes : list of tuples
        List of the newly added nodes in the same order in `lines`.

    """
    if NWN.graph["type"] != "JDA":
        raise NotImplementedError("Only JDA is currently supported")

    new_wire_num = len(lines)

    if new_wire_num != len(electrodes):
        raise ValueError(
            "Length of new lines list must equal length of electrode boolean list."
        )

    # Update wire number in NWN
    start_ind = NWN.graph["wire_num"]
    NWN.graph["wire_num"] += new_wire_num

    # Keep track of new wires
    new_wire_nodes = []

    # Add wires to NWN
    for i in range(new_wire_num):
        # Create new node
        NWN.graph["lines"].append(lines[i])
        new_wire_nodes.append((start_ind + i,))
        NWN.add_node((start_ind + i,), electrode=electrodes[i])

        # Keep track of the electrodes
        if electrodes[i]:
            NWN.graph["electrode_list"].append((start_ind + i,))

        # Find intersects
        intersect_dict = find_line_intersects(start_ind + i, NWN.graph["lines"])

        # Add edges to NWN
        conductance = (
            1 / resistance
            if resistance is not None
            else NWN.graph["junction_conductance"]
        )
        NWN.add_edges_from(
            [((key[0],), (key[1],)) for key in intersect_dict.keys()],
            conductance=conductance,
            capacitance=NWN.graph["junction_capacitance"],
            type="junction",
        )
        NWN.graph["loc"].update(intersect_dict)

        # Update index lookup
        NWN.graph["node_indices"].update({(start_ind + i,): start_ind + i})

    # Update wire density
    area = NWN.graph["length"] * NWN.graph["width"]
    NWN.graph["wire_density"] = (
        NWN.graph["wire_num"] - len(NWN.graph["electrode_list"])
    ) / area

    # Clear current junction list
    del NWN.wire_junctions

    return new_wire_nodes

convert_NWN_to_MNR

convert_NWN_to_MNR(NWN: NanowireNetwork)

Converts a JDA NWN to an MNR NWN in-place.

Parameters:

Name Type Description Default
NWN Graph

JDA nanowire network.

required
Source code in mnns/nanowires.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def convert_NWN_to_MNR(NWN: NanowireNetwork):
    """
    Converts a JDA NWN to an MNR NWN in-place.

    Parameters
    ----------
    NWN : nx.Graph
        JDA nanowire network.

    """
    if NWN.graph["type"] == "MNR":
        print("Nanowire network already MNR.")
        return

    NWN.graph["type"] = "MNR"
    l0 = NWN.graph["units"]["l0"]
    rho0 = NWN.graph["units"]["rho0"]
    Ron = NWN.graph["units"]["Ron"]
    D0 = NWN.graph["units"]["D0"]
    A0 = D0 * D0

    rho = NWN.graph["wire_resistivity"]
    D = NWN.graph["wire_diameter"]
    A = np.pi / 4 * D * D

    for i in range(NWN.graph["wire_num"]):
        # Get the junctions for a wire
        junctions = NWN.edges((i,))

        # Get location of the junction for a wire
        junction_locs = {
            edge: NWN.graph["loc"][tuple(sorted([edge[0][0], edge[1][0]]))]
            for edge in junctions
        }

        # Add junctions as part of the LineString that makes up the wire
        NWN.graph["lines"][i], ordering = add_points_to_line(
            NWN.graph["lines"][i], junction_locs.values(), return_ordering=True
        )

        # If wire is electrode, move on to the next wire
        if (i,) in NWN.graph["electrode_list"]:
            continue

        # Split nodes into subnodes representing the junctions on the wires
        for j, (edge, loc) in enumerate(junction_locs.items()):
            # Find connecting node
            other_node = edge[~edge.index((i,))]

            # Get old edge attributes
            old_attributes = NWN.edges[edge]

            # Create the replacing MNR node and edge
            NWN.add_node((i, j), loc=loc, electrode=False)
            NWN.add_edge((i, j), other_node, **old_attributes)

        # Remove old JDA node, only if it had connections
        if len(junction_locs) >= 1:
            NWN.remove_node((i,))

        # Add edges between subnodes
        for ind, next_ind in zip(ordering, ordering[1:]):
            # Find inner-wire resistance
            L = NWN.nodes[(i, ind)]["loc"].distance(
                NWN.nodes[(i, next_ind)]["loc"]
            )
            wire_conductance = (Ron * A0 * A) / (rho0 * l0 * rho * L * 1e3)

            # Add inner-wire edge
            NWN.add_edge(
                (i, ind),
                (i, next_ind),
                conductance=wire_conductance,
                capacitance=0,
                type="inner",
            )

    # Update index lookup
    NWN.graph["node_indices"] = {
        node: ind for ind, node in enumerate(sorted(NWN.nodes()))
    }

get_edge_indices

get_edge_indices(
    NWN: NanowireNetwork, edges: list[NWNEdge]
) -> tuple[list[NWNNodeIndex], list[NWNNodeIndex]]

Given a NWN and a list of edges, returns two lists: one of the indices of the first nodes in the input edge list, and one of the second.

Parameters:

Name Type Description Default
NWN Graph

Nanowire Network.

required
edges list of tuples

List of edges to find the indices of.

required
Source code in mnns/nanowires.py
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
def get_edge_indices(
    NWN: NanowireNetwork, edges: list[NWNEdge]
) -> tuple[list[NWNNodeIndex], list[NWNNodeIndex]]:
    """
    Given a NWN and a list of edges, returns two lists: one of the indices of
    the first nodes in the input edge list, and one of the second.

    Parameters
    ----------
    NWN : Graph
        Nanowire Network.

    edges : list of tuples
        List of edges to find the indices of.

    """
    # JDA edge indices
    if NWN.graph["type"] == "JDA":
        start_nodes, end_nodes = map(
            list, zip(*[(*n1, *n2) for n1, n2 in edges])
        )

    # MNR edge indices
    elif NWN.graph["type"] == "MNR":
        tmp = []
        for key in NWN.graph["node_indices"].keys():
            if len(key) == 2:
                tmp.append(key[1])
            else:
                tmp.append(0)

        node_start_index = np.where(np.asarray(tmp) == 0)[0]

        start_nodes, end_nodes = [], []
        for n1, n2 in edges:
            if len(n1) == 2:
                start_nodes.append(node_start_index[n1[0]] + n1[1])
            else:
                start_nodes.append(node_start_index[n1[0]])
            if len(n2) == 2:
                end_nodes.append(node_start_index[n2[0]] + n2[1])
            else:
                end_nodes.append(node_start_index[n2[0]])

    # Invalid NWN Type
    else:
        raise ValueError("Invalid NWN type.")

    return start_nodes, end_nodes