UC-Davis-molecular-computing/nuad

Domains and pools should be stored per-design

cgevans opened this issue · 1 comments

It appears that domains and pools are being stored globally in some way, rather than being connected to a particular design.

This means that, when creating a new design that has domains with the same names as an old design, some properties of the domains in the old design will be carried over to the new. In particular, pools will be, which means that setting a pool will fail. As a minimal example:

import nuad.constraints as nc

s = nc.Strand(["a", "b*"], name="test")
des = nc.Design([s])
poolA = nc.DomainPool("test", 10)
des.domains_by_name["a"].pool = poolA  # Works

s = nc.Strand(["a", "b*"], name="test")
des = nc.Design([s])
poolB = nc.DomainPool("test", 10)
des.domains_by_name["a"].pool = poolB  # Causes ValueError because pool is already poolA.

Yes, this is by design, although maybe there's a better way to do it.

When you use the Strand constructor with the domain_names parameter (which is the first positional parameter, so it's being used in your example), it looks up a global variable _domains_interned:

_domains_interned: Dict[str, Domain] = {}

that stores Domain objects to see if any of them has that domain name and reuses the same Domain object if so:

nuad/nuad/constraints.py

Lines 2082 to 2087 in 01e28ff

domain: Domain
if domain_name not in _domains_interned:
domain = Domain(name=domain_name)
_domains_interned[domain_name] = domain
else:
domain = _domains_interned[domain_name]

Ideally you are correct that this would be stored in the Design object rather than globally, but it would have to be redesigned to avoid using the Strand constructor, which may be called before there is even a Design object.

Perhaps one fix is to replace the Strand constructor with a method Design.add_strand(), which takes the same arguments as the current Strand constructor?