Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9762716
update default parameter files, including comments
murrayrm Nov 25, 2025
4402cf1
use mechanisms/, mixtures/ for default parameters in most places
murrayrm Nov 26, 2025
17eb058
RNAse, RNAase --> RNase
murrayrm Nov 26, 2025
b33d216
update extract parameters and mechanism to give reasonable expression…
murrayrm Nov 28, 2025
ff32483
update PURE parameters and mechanisms to give reasonable expression l…
murrayrm Nov 28, 2025
e730f69
Add missing PURE parameters + comments for TetR
murrayrm Nov 30, 2025
77206f9
fix issue with multiple toctrees
murrayrm Dec 22, 2025
ba8bc5b
add default parameters to mechanisms
murrayrm Dec 22, 2025
a89efb1
add unit alaiases + checks, tests
murrayrm Dec 22, 2025
831798d
add default parameter files for mechanisms
murrayrm Dec 25, 2025
9e48299
allow part_id to be a list; add binding subtypes
murrayrm Dec 26, 2025
79f6cfc
change 'cooperative_binding' mechanism type to 'binding'
murrayrm Dec 26, 2025
c1c77c1
add missing reverse energy reaction for energy translation
murrayrm Dec 26, 2025
e18e71b
add documentation + code cleanup
murrayrm Dec 27, 2025
469ec28
add (default) parameter files for txtl mechanisms
murrayrm Dec 28, 2025
fbf03d9
add default parameter_file for transport mechanisms
murrayrm Dec 28, 2025
714d5ec
consistent capitalization of BioCRNpyler
murrayrm Dec 28, 2025
32b4302
add default parameter_file for enzyme mechanisms
murrayrm Dec 28, 2025
8869b5d
convert parameter files to pure ASCII
murrayrm Dec 29, 2025
b84d850
fix sbml unit management with the new unit updates in pr320, add othe…
ayush9pandey Apr 26, 2026
22e2ce9
remove unnecessary test in unit params
ayush9pandey Apr 26, 2026
2d9fee8
typo in workflow and add aliases for nL, mL so tests pass
ayush9pandey Apr 27, 2026
2f14b63
ruff format
murrayrm May 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov python-libsbml
- name: Tnstall biocrnpyler
- name: Install biocrnpyler
run: pip install -e .
- name: Test biocrnpyler
run: pytest --cov biocrnpyler
Expand Down
3 changes: 3 additions & 0 deletions Tests/Combinatorial/test_construct_combinatorially.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ def test_combinatorial_DNAconstruct_in_Mixtures():
for mclass, args in mixture_classes:
# create the Mixture with parameters
args['parameters'] = dict(parameters)
args['parameter_file'] = None
m = mclass(**args)

# Promoters and terminators are instances
Expand Down Expand Up @@ -221,6 +222,7 @@ def test_combinatorial_RNAconstruct_in_Mixtures():
for mclass, args in mixture_classes:
# create the Mixture with parameters
args['parameters'] = dict(parameters)
args['parameter_file'] = None
m = mclass(**args)

# RBSs and CDSs are instances
Expand Down Expand Up @@ -252,6 +254,7 @@ def test_combinatorial_DNAconstruct_RNAconstruct_in_Mixtures():
for mclass, args in mixture_classes:
# create the Mixture with parameters
args['parameters'] = dict(parameters)
args['parameter_file'] = None
m = mclass(**args)

# Promoters and terminators are instances
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,12 @@ def setUp(self) -> None:
self.parameters = {
'kb': 1.0,
'ku': 1.0,
'kb_ntps': 1.0,
'ku_ntps': 1.0,
'ktx': 1.0,
'ktl': 1.0,
'kb_fuel': 1.0,
'ku_fuel': 1.0,
'kdeg': 1.0,
'kdil': 1.0,
'kexpress': 1.0,
Expand Down
3 changes: 1 addition & 2 deletions Tests/Unit/test_compartment.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ def test_compartment_setter(self):
s1 = Species('S1', compartment=comp1)
mixture_1 = Mixture(species=[s1])
crn_1 = mixture_1.compile_crn()
crn_1.write_sbml_file('test_compartment_setter_1.xml')
# change compartment
s1.compartment = comp2
mixture_2 = Mixture(species=[s1])
Expand All @@ -135,7 +134,7 @@ def test_compartment_in_special_mixtures(self):
)
E = EnergyTxTlExtract(
components=[activatable_assembly],
parameter_file='examples/Specialized Tutorials/txtl_toolbox_parameters.txt',
parameter_file='mixtures/extract_parameters.tsv',
)
CRN_1 = E.compile_crn() # compile CRN

Expand Down
195 changes: 177 additions & 18 deletions Tests/Unit/test_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def test_parameter_entry(self):
parameter_value=1.0,
parameter_key={'part_id': 'id'},
parameter_info={'comment': 'comment', 'unit': 'M'},
unit='m',
unit='uM',
)

# Invalid keys
Expand Down Expand Up @@ -516,7 +516,7 @@ def test_find_parameter(self):
)


def test_findpath():
def test_findpath(tmp_path):
import os
import platform

Expand All @@ -527,21 +527,180 @@ def test_findpath():

# Make sure that files in package can be found
assert os.path.exists(
bcp.find_file_in_bcp_path('components/tetr_parameters.tsv')
bcp.find_file_in_bcp_path('mechanisms/txtl_parameters.tsv')
)

# Make sure we can find files in current directory
assert bcp.find_file_in_bcp_path('__testfile__.tsv') is None
open('__testfile__.tsv', 'w')
assert os.path.exists(bcp.find_file_in_bcp_path('__testfile__.tsv'))
os.remove('__testfile__.tsv')

# Make sure we can find files in the path
open('../__testfile__.tsv', 'w')
assert bcp.find_file_in_bcp_path('__testfile__.tsv') is None
if platform.system() == 'Windows':
os.environ['BCP_PATH'] = '/tmp;.;..'
else:
os.environ['BCP_PATH'] = '/tmp:.:..'
assert os.path.exists(bcp.find_file_in_bcp_path('__testfile__.tsv'))
os.remove('../__testfile__.tsv')
prev_cwd = os.getcwd()
prev_bcp_path = os.environ.get('BCP_PATH')
child_dir = tmp_path / 'child'
child_dir.mkdir()
os.chdir(child_dir)
try:
# Make sure we can find files in current directory
assert bcp.find_file_in_bcp_path('__testfile__.tsv') is None
(child_dir / '__testfile__.tsv').write_text('')
assert os.path.exists(bcp.find_file_in_bcp_path('__testfile__.tsv'))
os.remove(child_dir / '__testfile__.tsv')

# Make sure we can find files in the path
(tmp_path / '__testfile__.tsv').write_text('')
assert bcp.find_file_in_bcp_path('__testfile__.tsv') is None
if platform.system() == 'Windows':
os.environ['BCP_PATH'] = f'/tmp;.;{tmp_path}'
else:
os.environ['BCP_PATH'] = f'/tmp:.:{tmp_path}'
assert os.path.exists(bcp.find_file_in_bcp_path('__testfile__.tsv'))
finally:
os.chdir(prev_cwd)
if prev_bcp_path is None:
os.environ.pop('BCP_PATH', None)
else:
os.environ['BCP_PATH'] = prev_bcp_path


def test_parameter_ordering():
import biocrnpyler as bcp

# Create the components for testing
TetR = bcp.Protein('TetR')
aTc = bcp.Species('aTc')
TetR_inactive=bcp.ChemicalComplex([TetR.species, aTc])
ptet = bcp.RegulatedPromoter('ptet', TetR)
dna_GFP = bcp.DNAassembly(
'GFP', promoter=ptet, rbs='RBS', protein='GFP')

# Default case: make sure we can compile with no additional paramters
default_mixture = bcp.BasicPURE(
'default', components=[dna_GFP, TetR_inactive])
default_crn = default_mixture.compile_crn()

# Find the binding reactions of TetR to DNA and aTc
aTc_TetR_index = dna_TetR_index = -1
for i, reaction in enumerate(default_crn.reactions):
if not isinstance(reaction.propensity_type, bcp.MassAction):
continue

inputs = [input.species for input in reaction.inputs]

# Find the binding of aTc to TetR
if aTc in inputs and TetR.species in inputs:
aTc_TetR_index = i

# Find the binding of TetR to DNA
if dna_GFP.species in inputs and TetR.species in inputs:
dna_TetR_index = i

assert aTc_TetR_index != -1 and dna_TetR_index != -1
assert default_crn.reactions[
dna_TetR_index].propensity_type.k_forward != 1
assert default_crn.reactions[
aTc_TetR_index].propensity_type.k_forward != 1

# Rebuild using given parameters
baseline_parameters = {
('binding', None, 'kb'): 1,
('binding', None, 'ku'): 0.1,
('binding', None, 'cooperativity'): 2,
}
TetR_inactive=bcp.ChemicalComplex(
[TetR.species, aTc], parameters=baseline_parameters)
dna_GFP = bcp.DNAassembly(
'GFP', promoter=ptet, rbs='RBS', protein='GFP',
parameters=baseline_parameters)
baseline_mixture = bcp.BasicPURE(
'baseline', components=[dna_GFP, TetR_inactive])
baseline_crn = baseline_mixture.compile_crn()
assert baseline_crn.reactions[
dna_TetR_index].propensity_type.k_forward == 1
assert baseline_crn.reactions[
aTc_TetR_index].propensity_type.k_forward == 1

# Override using mechanism subtypes
mechtype_parameters = baseline_parameters | {
('binding', 'dna_protein', 'kb'): 2,
('binding', 'chemical_complex', 'kb'): 3,
}
TetR_inactive=bcp.ChemicalComplex(
[TetR.species, aTc], parameters=mechtype_parameters)
dna_GFP = bcp.DNAassembly(
'GFP', promoter=ptet, rbs='RBS', protein='GFP',
parameters=mechtype_parameters)
mechtype_mixture = bcp.BasicPURE(
'mechtype', components=[dna_GFP, TetR_inactive])
mechtype_crn = mechtype_mixture.compile_crn()
assert mechtype_crn.reactions[
dna_TetR_index].propensity_type.k_forward == 2
assert mechtype_crn.reactions[
aTc_TetR_index].propensity_type.k_forward == 3

# Override using part IDs
partid_parameters = mechtype_parameters | {
('binding', 'ptet_TetR', 'kb'): 4,
('binding', 'aTc_protein_TetR', 'kb'): 5,
}
TetR_inactive=bcp.ChemicalComplex(
[TetR.species, aTc], parameters=partid_parameters)
dna_GFP = bcp.DNAassembly(
'GFP', promoter=ptet, rbs='RBS', protein='GFP',
parameters=partid_parameters)
partid_mixture = bcp.BasicPURE(
'partid', components=[dna_GFP, TetR_inactive])
partid_crn = partid_mixture.compile_crn()
assert partid_crn.reactions[
dna_TetR_index].propensity_type.k_forward == 4
assert partid_crn.reactions[
aTc_TetR_index].propensity_type.k_forward == 5

# Override using mechanism name
mechname_parameters = baseline_parameters | {
('one_step_cooperative_binding', None, 'kb'): 6,
('binding', 'chemical_complex', 'kb'): 7,
}
TetR_inactive=bcp.ChemicalComplex(
[TetR.species, aTc], parameters=mechname_parameters)
dna_GFP = bcp.DNAassembly(
'GFP', promoter=ptet, rbs='RBS', protein='GFP',
parameters=mechname_parameters)
mechname_mixture = bcp.BasicPURE(
'mechname', components=[dna_GFP, TetR_inactive])
mechname_crn = mechname_mixture.compile_crn()
assert mechname_crn.reactions[
dna_TetR_index].propensity_type.k_forward == 6
assert mechname_crn.reactions[
aTc_TetR_index].propensity_type.k_forward == 7

# Override in mixture instead of components
mixture_parameters = baseline_parameters | {
('one_step_cooperative_binding', None, 'kb'): 8,
('binding', 'chemical_complex', 'kb'): 9,
}
TetR_inactive=bcp.ChemicalComplex([TetR.species, aTc])
dna_GFP = bcp.DNAassembly(
'GFP', promoter=ptet, rbs='RBS', protein='GFP')
mixture_mixture = bcp.BasicPURE(
'mixture', components=[dna_GFP, TetR_inactive],
parameters=mixture_parameters)
mixture_crn = mixture_mixture.compile_crn()
assert mixture_crn.reactions[
dna_TetR_index].propensity_type.k_forward == 8
assert mixture_crn.reactions[
aTc_TetR_index].propensity_type.k_forward == 9

# Override in components and not mixture
component_parameters = baseline_parameters | {
('one_step_cooperative_binding', None, 'kb'): 10,
('binding', 'chemical_complex', 'kb'): 11,
}
TetR_inactive=bcp.ChemicalComplex(
[TetR.species, aTc], parameters=component_parameters)
dna_GFP = bcp.DNAassembly(
'GFP', promoter=ptet, rbs='RBS', protein='GFP',
parameters=component_parameters)
mixture_mixture = bcp.BasicPURE(
'mixture', components=[dna_GFP, TetR_inactive],
parameters=mixture_parameters)
mixture_crn = mixture_mixture.compile_crn()
assert mixture_crn.reactions[
dna_TetR_index].propensity_type.k_forward == 10
assert mixture_crn.reactions[
aTc_TetR_index].propensity_type.k_forward == 11
3 changes: 2 additions & 1 deletion Tests/Unit/test_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,8 @@ def test_render_network_bokeh():
('rna_degradation_mm', None, 'kdeg'): 0.001,
(None, None, 'cooperativity'): 2,
}
txtl = bcp.TxTlExtract('mixture1', parameters=parameters)
txtl = bcp.TxTlExtract(
'mixture1', parameters=parameters, overwrite_parameters=True)
dna = bcp.DNAassembly(
'mydna',
promoter=bcp.RegulatedPromoter('plac', ['laci']),
Expand Down
37 changes: 32 additions & 5 deletions Tests/Unit/test_sbml.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,36 @@ def test_generate_sbml_model():
assert validate_sbml(document) == 0


def test_generate_sbml_model_writes_units_from_biocrnpyler():
compartment = Compartment('cell', size=1, unit='uL')
s1 = Species('S1', compartment=compartment)
s2 = Species('S2', compartment=compartment)
kf = ParameterEntry('k', 0.1, unit='/sec')
x0 = ParameterEntry('S1_0', 1.0, unit='uM')
rxn = Reaction.from_massaction(inputs=[s1], outputs=[s2], k_forward=kf)
crn = ChemicalReactionNetwork(
species=[s1, s2],
reactions=[rxn],
initial_concentration_dict={s1: x0},
)

document, model = crn.generate_sbml_model()
assert validate_sbml(document) == 0

sbml_compartment = model.getCompartment('cell')
assert sbml_compartment.getUnits() == 'uL'

sbml_species = model.getSpecies(0)
assert sbml_species.getSubstanceUnits() == 'pmol'
assert sbml_species.getInitialConcentration() == 1.0

sbml_parameter = model.getListOfParameters()[0]
assert sbml_parameter.getUnits() == 'per_sec'

unit_defs = {ud.getId() for ud in model.getListOfUnitDefinitions()}
assert {'uL', 'pmol', 'per_sec'}.issubset(unit_defs)


def test_generate_sbml_model_parameter_names():
s1 = Species('S1')
s2 = Species('S2')
Expand Down Expand Up @@ -518,11 +548,8 @@ def check(value, message):
global_param = _create_global_parameter(
model, 'k_global', value=10, p_unit=24
)
with pytest.warns(
Warning,
match="The string identifier for the unit 1_s is not supported by "
"BioCRNpyler. Add this to the dictionary in biocrnpyler/units.py "
"if you want this unit",
with pytest.raises(
ValueError, match="Unsupported SBML unit '1_s'"
):
global_param = _create_global_parameter(
model, 'k_global', value=10, p_unit='1_s'
Expand Down
Loading
Loading