def __init__(self, primers, template, limit=13, **kwargs): r"""The Anneal class has to be initiated with at least an iterable of primers and a template. Parameters ---------- primers : iterable of :class:`Primer` or Biopython SeqRecord like objects Primer sequences 5'-3'. template : Dseqrecord The template sequence 5'-3'. limit : int, optional limit length of the annealing part of the primers. Attributes ---------- products: list A list of Amplicon objects, one for each primer pair that may form a PCR product. Examples -------- >>> from pydna.readers import read >>> from pydna.amplify import Anneal >>> from pydna.dseqrecord import Dseqrecord as Ds >>> t = Ds("tacactcaccgtctatcattatcta" ... "ctatcgactgtatcatctgatagcac") >>> from Bio.SeqRecord import SeqRecord >>> p1 = read(">p1\ntacactcaccgtctatcattatc", ds = False) >>> p2 = read(">p2\ngtgctatcagatgatacagtcg", ds = False) >>> ann = Anneal((p1, p2), t) >>> print(ann.report()) Template name 51 nt linear: p1 anneals forward (--->) at 23 p2 anneals reverse (<---) at 29 >>> ann.products [Amplicon(51)] >>> amplicon_list = ann.products >>> amplicon = amplicon_list.pop() >>> amplicon Amplicon(51) >>> print(amplicon.figure()) 5tacactcaccgtctatcattatc...cgactgtatcatctgatagcac3 |||||||||||||||||||||| 3gctgacatagtagactatcgtg5 5tacactcaccgtctatcattatc3 ||||||||||||||||||||||| 3atgtgagtggcagatagtaatag...gctgacatagtagactatcgtg5 >>> print(amplicon) Dseqrecord circular: False size: 51 ID: 51bp U96-TO06Y6pFs74SQx8M1IVTBiY Name: 51bp_PCR_prod Description: pcr product_p1_p2 Number of features: 2 /molecule_type=DNA Dseq(-51) taca..gcac atgt..cgtg >>> print(amplicon.program()) <BLANKLINE> |95°C|95°C | |tmf:59.5 |____|_____ 72°C|72°C|tmr:59.7 |5min|30s \ 47.7°C _____|____|30s/kb | | \______/ 0: 1|5min|GC 39% | | 30s | |51bp >>> """ self.primers = primers self.template = _copy.deepcopy(template) self.limit = limit self.kwargs = kwargs self._products = None self.forward_primers = [] self.reverse_primers = [] twl = len(self.template.seq.watson) tcl = len(self.template.seq.crick) if self.template.linear: tw = self.template.seq.watson tc = self.template.seq.crick else: tw = self.template.seq.watson + self.template.seq.watson tc = self.template.seq.crick + self.template.seq.crick for p in self.primers: self.forward_primers.extend(( _Primer( p, # template = self.template, position=tcl - pos - min(self.template.seq.ovhg, 0), footprint=fp, ) for pos, fp in _annealing_positions(str(p.seq), tc, self.limit) if pos < tcl)) self.reverse_primers.extend(( _Primer( p, # template = self.template, position=pos + max(0, self.template.seq.ovhg), footprint=fp, ) for pos, fp in _annealing_positions(str(p.seq), tw, self.limit) if pos < twl)) self.forward_primers.sort(key=_operator.attrgetter("position")) self.reverse_primers.sort(key=_operator.attrgetter("position"), reverse=True) for fp in self.forward_primers: if fp.position - fp._fp >= 0: start = fp.position - fp._fp end = fp.position self.template.features.append( _SeqFeature( _FeatureLocation(start, end), type="primer_bind", strand=1, qualifiers={ "label": [fp.name], "ApEinfo_fwdcolor": ["#baffa3"], "ApEinfo_revcolor": ["#ffbaba"], }, )) else: start = len(self.template) - fp._fp + fp.position end = start + fp._fp - len(self.template) sf = _SeqFeature( _CompoundLocation([ _FeatureLocation(start, len(self.template)), _FeatureLocation(0, end), ]), type="primer_bind", location_operator="join", qualifiers={ "label": [fp.name], "ApEinfo_fwdcolor": ["#baffa3"], "ApEinfo_revcolor": ["#ffbaba"], }, ) self.template.features.append(sf) for rp in self.reverse_primers: if rp.position + rp._fp <= len(self.template): start = rp.position end = rp.position + rp._fp self.template.features.append( _SeqFeature( _FeatureLocation(start, end), type="primer_bind", strand=-1, qualifiers={ "label": [rp.name], "ApEinfo_fwdcolor": ["#baffa3"], "ApEinfo_revcolor": ["#ffbaba"], }, )) else: start = rp.position end = rp.position + rp._fp - len(self.template) self.template.features.append( _SeqFeature( _CompoundLocation([ _FeatureLocation(0, end), _FeatureLocation(start, len(self.template)), ]), type="primer_bind", location_operator="join", strand=-1, qualifiers={"label": [rp.name]}, ))
def set_forward_primer_footprint(self, length): self.forward_primer = _Primer(self.forward_primer.tail + self.seq[:length], footprint=length)
def set_reverse_primer_footprint(self, length): self.reverse_primer = _Primer(self.reverse_primer.tail + self.seq[:length], footprint=length)
def read_primer(data): """Use this function to read a primer sequence from a string or a local file. The usage is similar to the :func:`parse_primer` function.""" return _Primer(read(data, ds=False))
def parse_primers(data): """ """ return [_Primer(x) for x in parse(data, ds=False)]
def __init__( self, primers, template, limit=13, primerc=1000.0, # nM saltc=50, # mM **kwargs): r'''The Anneal class has to be initiated with at least an iterable of primers and a template. Parameters ---------- primers : iterable of :class:`Primer` or Biopython SeqRecord like objects Primer sequences 5'-3'. template : Dseqrecord The template sequence 5'-3'. limit : int, optional limit length of the annealing part of the primers. fprimerc : float, optional Concentration of forward primer in nM, set to 1000.0 nM by default rprimerc : float, optional Concentration of reverse primer in nM, set to 1000.0 nM by default saltc : float, optional Salt concentration (monovalet cations) :mod:`tmbresluc` set to 50.0 mM by default Attributes ---------- products: list A list of Amplicon objects, one for each primer pair that may form a PCR product. Examples -------- >>> from pydna.readers import read >>> from pydna.amplify import Anneal >>> from pydna.dseqrecord import Dseqrecord >>> template = Dseqrecord("tacactcaccgtctatcattatctactatcgactgtatcatctgatagcac") >>> from Bio.SeqRecord import SeqRecord >>> p1 = read(">p1\ntacactcaccgtctatcattatc", ds = False) >>> p2 = read(">p2\ngtgctatcagatgatacagtcg", ds = False) >>> ann = Anneal((p1, p2), template) >>> print(ann.report()) Template name 51 nt linear: Primer p1 anneals forward at position 23 <BLANKLINE> Primer p2 anneals reverse at position 29 >>> ann.products [Amplicon(51)] >>> amplicon_list = ann.products >>> amplicon = amplicon_list.pop() >>> amplicon Amplicon(51) >>> print(amplicon.figure()) 5tacactcaccgtctatcattatc...cgactgtatcatctgatagcac3 |||||||||||||||||||||| tm 55.9 (dbd) 60.5 3gctgacatagtagactatcgtg5 5tacactcaccgtctatcattatc3 ||||||||||||||||||||||| tm 54.6 (dbd) 58.8 3atgtgagtggcagatagtaatag...gctgacatagtagactatcgtg5 >>> amplicon.annotations['date'] = '02-FEB-2013' # Set the date for this example to pass the doctest >>> print(amplicon) Dseqrecord circular: False size: 51 ID: 51bp U96-TO06Y6pFs74SQx8M1IVTBiY Name: 51bp_PCR_prod Description: pcr product_p1_p2 Number of features: 2 /date=02-FEB-2013 Dseq(-51) taca..gcac atgt..cgtg >>> print(amplicon.program()) <BLANKLINE> Taq (rate 30 nt/s) 35 cycles |51bp 95.0°C |95.0°C | |Tm formula: Biopython Tm_NN |_________|_____ 72.0°C |72.0°C|SaltC 50mM | 03min00s|30s \ ________|______|Primer1C 1.0µM | | \ 45.4°C/ 0min 2s| 5min |Primer2C 1.0µM | | \_____/ | |GC 39% | | 30s | |4-12°C >>> ''' self.primers = primers self.primerc = primerc self.saltc = saltc self.template = _copy.deepcopy(template) self.limit = limit self.kwargs = defaultdict(str, kwargs) self._products = None self.forward_primers = [] self.reverse_primers = [] twl = len(self.template.seq.watson) tcl = len(self.template.seq.crick) if self.template.linear: tw = self.template.seq.watson tc = self.template.seq.crick else: tw = self.template.seq.watson + self.template.seq.watson tc = self.template.seq.crick + self.template.seq.crick for p in self.primers: self.forward_primers.extend(( _Primer(p, position=tcl - pos - min(self.template.seq.ovhg, 0), footprint=fp) for pos, fp in _annealing_positions(str(p.seq), tc, self.limit) if pos < tcl)) self.reverse_primers.extend(( _Primer(p, position=pos + max(0, self.template.seq.ovhg), footprint=fp) for pos, fp in _annealing_positions(str(p.seq), tw, self.limit) if pos < twl)) self.forward_primers.sort(key=_operator.attrgetter('position')) self.reverse_primers.sort(key=_operator.attrgetter('position'), reverse=True) for fp in self.forward_primers: if fp.position - fp._fp >= 0: start = fp.position - fp._fp end = fp.position self.template.features.append( _SeqFeature(_FeatureLocation(start, end), type="primer_bind", strand=1, qualifiers={ "label": [fp.name], "ApEinfo_fwdcolor": ["#baffa3"], "ApEinfo_revcolor": ["#ffbaba"] })) else: start = len(self.template) - fp._fp + fp.position end = start + fp._fp - len(self.template) sf = _SeqFeature(_CompoundLocation([ _FeatureLocation(start, len(self.template)), _FeatureLocation(0, end) ]), type="primer_bind", location_operator="join", qualifiers={ "label": [fp.name], "ApEinfo_fwdcolor": ["#baffa3"], "ApEinfo_revcolor": ["#ffbaba"] }) self.template.features.append(sf) for rp in self.reverse_primers: if rp.position + rp._fp <= len(self.template): start = rp.position end = rp.position + rp._fp self.template.features.append( _SeqFeature(_FeatureLocation(start, end), type="primer_bind", strand=-1, qualifiers={ "label": [rp.name], "ApEinfo_fwdcolor": ["#baffa3"], "ApEinfo_revcolor": ["#ffbaba"] })) else: start = rp.position end = rp.position + rp._fp - len(self.template) self.template.features.append( _SeqFeature(_CompoundLocation([ _FeatureLocation(0, end), _FeatureLocation(start, len(self.template)) ]), type="primer_bind", location_operator="join", strand=-1, qualifiers={"label": [rp.name]}))
def primer_design( template, fp=None, rp=None, target_tm=55.0, fprimerc=1000.0, # nM rprimerc=1000.0, # nM saltc=50.0, limit=13, formula = _tmbresluc): '''This function designs a forward primer and a reverse primer for PCR amplification of a given template sequence. The template argument is a Dseqrecord object or equivalent containing the template sequence. The optional fp and rp arguments can contain an existing primer for the sequence (either the forward or reverse primer). One or the other primers can be specified, not both (since then there is nothing to design!, use the pydna.amplify.pcr function instead). If one of the primers is given, the other primer is designed to match in terms of Tm. If both primers are designed, they will be designed to target_tm fprimerc, rprimerc and saltc are formward and reverse primer concentration (nM). Saltc is the salt concentration. These arguments might affect how Tm is calculated. formula is a function that can take at least three arguments f( str, primerc=float, saltc=float). There are several of these in the pydna.tm module. The function returns a pydna.amplicon.Amplicon class instance. This object has the object.forward_primer and object.reverse_primer properties which contain the designed primers. Parameters ---------- template : pydna.dseqrecord.Dseqrecord a Dseqrecord object. The only required argument. fp, rp : pydna.primer.Primer, optional optional pydna.primer.Primer objects containing one primer each. target_tm : float, optional target tm for the primers, set to 55°C by default. fprimerc : float, optional Concentration of forward primer in nM, set to 1000.0 nM by default. rprimerc : float, optional Concentration of reverse primer in nM, set to 1000.0 nM by default. saltc : float, optional Salt concentration (monovalet cations) :mod:`tmbresluc` set to 50.0 mM by default formula : function formula used for tm calculation this is the name of a function. built in options are: 1. :func:`pydna.amplify.tmbresluc` (default) 2. :func:`pydna.amplify.basictm` 3. :func:`pydna.amplify.tmstaluc98` 4. :func:`pydna.amplify.tmbreslauer86` These functions are imported from the :mod:`pydna.amplify` module, but can be substituted for some other custom made function. Returns ------- result : Amplicon Examples -------- >>> from pydna.dseqrecord import Dseqrecord >>> t=Dseqrecord("atgactgctaacccttccttggtgttgaacaagatcgacgacatttcgttcgaaacttacgatg") >>> t Dseqrecord(-64) >>> from pydna.design import primer_design >>> ampl = primer_design(t) >>> ampl Amplicon(64) >>> ampl.forward_primer f64 18-mer:5'-atgactgctaacccttcc-3' >>> ampl.reverse_primer r64 19-mer:5'-catcgtaagtttcgaacga-3' >>> print(ampl.figure()) 5atgactgctaacccttcc...tcgttcgaaacttacgatg3 ||||||||||||||||||| tm 53.8 (dbd) 60.6 3agcaagctttgaatgctac5 5atgactgctaacccttcc3 |||||||||||||||||| tm 54.4 (dbd) 58.4 3tactgacgattgggaagg...agcaagctttgaatgctac5 >>> pf = "GGATCC" + ampl.forward_primer >>> pr = "GGATCC" + ampl.reverse_primer >>> pf f64 24-mer:5'-GGATCCatgactgct..tcc-3' >>> pr r64 25-mer:5'-GGATCCcatcgtaag..cga-3' >>> from pydna.amplify import pcr >>> pcr_prod = pcr(pf, pr, t) >>> print(pcr_prod.figure()) 5atgactgctaacccttcc...tcgttcgaaacttacgatg3 ||||||||||||||||||| tm 53.8 (dbd) 60.6 3agcaagctttgaatgctacCCTAGG5 5GGATCCatgactgctaacccttcc3 |||||||||||||||||| tm 54.4 (dbd) 58.4 3tactgacgattgggaagg...agcaagctttgaatgctac5 >>> print(pcr_prod.seq) GGATCCatgactgctaacccttccttggtgttgaacaagatcgacgacatttcgttcgaaacttacgatgGGATCC >>> from pydna.primer import Primer >>> pf = Primer("atgactgctaacccttccttggtgttg", id="myprimer") >>> ampl = primer_design(t, fp = pf) >>> ampl.forward_primer myprimer 27-mer:5'-atgactgctaaccct..ttg-3' >>> ampl.reverse_primer r64 28-mer:5'-catcgtaagtttcga..gtc-3' ''' def design(target_tm, template): ''' returns a string ''' tmp=0 length=limit p = str(template.seq[:length]) while tmp<target_tm: length+=1 p = str(template.seq[:length]) tmp = formula(p.upper()) ps = p[:-1] tmps = formula(str(ps).upper()) _module_logger.debug(((p, tmp),(ps, tmps))) return min( ( abs(target_tm-tmp), p), (abs(target_tm-tmps), ps) )[1] if fp and not rp: fp = _Anneal((fp,), template).forward_primers.pop() target_tm = formula( str(fp.footprint), primerc=fprimerc, saltc=saltc) _module_logger.debug("forward primer given, design reverse primer:") rp = _Primer(design(target_tm, template.reverse_complement())) elif not fp and rp: rp = _Anneal([rp], template).reverse_primers.pop() target_tm = formula( str(rp.footprint), primerc=rprimerc, saltc=saltc) _module_logger.debug("reverse primer given, design forward primer:") fp = _Primer(design(target_tm, template)) elif not fp and not rp: _module_logger.debug("no primer given, design forward primer:") fp = _Primer((design(target_tm, template))) target_tm = formula( str(fp.seq), primerc=fprimerc, saltc=saltc) _module_logger.debug("no primer given, design reverse primer:") rp = _Primer(design(target_tm, template.reverse_complement())) else: raise ValueError("Specify maximum one of the two primers.") fp.concentration = fprimerc rp.concentration = rprimerc if fp.id == "id": #<unknown id> fp.id = "f{}".format(len(template)) if rp.id == "id": rp.id = "r{}".format(len(template)) if fp.name == "name": fp.name = "f{}".format(len(template)) if rp.name == "name": rp.name = "r{}".format(len(template)) fp.description = fp.id+' '+template.accession rp.description = rp.id+' '+template.accession ampl = _Anneal( (fp, rp), template, limit=limit) prod = ampl.products[0] if len(ampl.products)>1: import warnings as _warnings from pydna import _PydnaWarning _warnings.warn("designed primers do not yield a unique PCR product", _PydnaWarning) return prod
def primer_design(template, fp=None, rp=None, limit=13, target_tm=55.0, tm_func=_tm_default, **kwargs): """This function designs a forward primer and a reverse primer for PCR amplification of a given template sequence. The template argument is a Dseqrecord object or equivalent containing the template sequence. The optional fp and rp arguments can contain an existing primer for the sequence (either the forward or reverse primer). One or the other primers can be specified, not both (since then there is nothing to design!, use the pydna.amplify.pcr function instead). If one of the primers is given, the other primer is designed to match in terms of Tm. If both primers are designed, they will be designed to target_tm tm_func is a function that takes an ascii string representing an oligonuceotide as argument and returns a float. Some useful functions can be found in the :mod:`pydna.tm` module, but can be substituted for a custom made function. The function returns a pydna.amplicon.Amplicon class instance. This object has the object.forward_primer and object.reverse_primer properties which contain the designed primers. Parameters ---------- template : pydna.dseqrecord.Dseqrecord a Dseqrecord object. The only required argument. fp, rp : pydna.primer.Primer, optional optional pydna.primer.Primer objects containing one primer each. target_tm : float, optional target tm for the primers, set to 55°C by default. tm_func : function Function used for tm calculation. This function takes an ascii string representing an oligonuceotide as argument and returns a float. Some useful functions can be found in the :mod:`pydna.tm` module, but can be substituted for a custom made function. Returns ------- result : Amplicon Examples -------- >>> from pydna.dseqrecord import Dseqrecord >>> t=Dseqrecord("atgactgctaacccttccttggtgttgaacaagatcgacgacatttcgttcgaaacttacgatg") >>> t Dseqrecord(-64) >>> from pydna.design import primer_design >>> ampl = primer_design(t) >>> ampl Amplicon(64) >>> ampl.forward_primer f64 17-mer:5'-atgactgctaacccttc-3' >>> ampl.reverse_primer r64 19-mer:5'-catcgtaagtttcgaacga-3' >>> print(ampl.figure()) 5atgactgctaacccttc...tcgttcgaaacttacgatg3 ||||||||||||||||||| 3agcaagctttgaatgctac5 5atgactgctaacccttc3 ||||||||||||||||| 3tactgacgattgggaag...agcaagctttgaatgctac5 >>> pf = "GGATCC" + ampl.forward_primer >>> pr = "GGATCC" + ampl.reverse_primer >>> pf f64 23-mer:5'-GGATCCatgactgct..ttc-3' >>> pr r64 25-mer:5'-GGATCCcatcgtaag..cga-3' >>> from pydna.amplify import pcr >>> pcr_prod = pcr(pf, pr, t) >>> print(pcr_prod.figure()) 5atgactgctaacccttc...tcgttcgaaacttacgatg3 ||||||||||||||||||| 3agcaagctttgaatgctacCCTAGG5 5GGATCCatgactgctaacccttc3 ||||||||||||||||| 3tactgacgattgggaag...agcaagctttgaatgctac5 >>> print(pcr_prod.seq) GGATCCatgactgctaacccttccttggtgttgaacaagatcgacgacatttcgttcgaaacttacgatgGGATCC >>> from pydna.primer import Primer >>> pf = Primer("atgactgctaacccttccttggtgttg", id="myprimer") >>> ampl = primer_design(t, fp = pf) >>> ampl.forward_primer myprimer 27-mer:5'-atgactgctaaccct..ttg-3' >>> ampl.reverse_primer r64 37-mer:5'-catcgtaagtttcga..gtt-3' """ def design(target_tm, template): """ returns a string """ tmp = 0 length = limit p = str(template.seq[:length]) while tmp < target_tm: length += 1 p = str(template.seq[:length]) tmp = tm_func(p) ps = p[:-1] tmps = tm_func(str(ps)) _module_logger.debug(((p, tmp), (ps, tmps))) return min((abs(target_tm - tmp), p), (abs(target_tm - tmps), ps))[1] if fp and not rp: fp = _Anneal((fp, ), template).forward_primers.pop() target_tm = tm_func(fp.footprint) _module_logger.debug("forward primer given, design reverse primer:") rp = _Primer(design(target_tm, template.reverse_complement())) elif not fp and rp: rp = _Anneal([rp], template).reverse_primers.pop() target_tm = tm_func(rp.footprint) _module_logger.debug("reverse primer given, design forward primer:") fp = _Primer(design(target_tm, template)) elif not fp and not rp: _module_logger.debug("no primer given, design forward primer:") fp = _Primer((design(target_tm, template))) target_tm = tm_func(str(fp.seq)) _module_logger.debug("no primer given, design reverse primer:") rp = _Primer(design(target_tm, template.reverse_complement())) else: raise ValueError("Specify maximum one of the two primers.") if fp.id == "id": # <unknown id> fp.id = "f{}".format(len(template)) if rp.id == "id": rp.id = "r{}".format(len(template)) if fp.name == "name": fp.name = "f{}".format(len(template)) if rp.name == "name": rp.name = "r{}".format(len(template)) fp.description = fp.id + " " + template.accession rp.description = rp.id + " " + template.accession ampl = _Anneal((fp, rp), template, limit=limit) prod = ampl.products[0] if len(ampl.products) > 1: import warnings as _warnings from pydna import _PydnaWarning _warnings.warn("designed primers do not yield a unique PCR product", _PydnaWarning) return prod