1 %%%%%%%%%%%%%%%%%%%%%%%%% Start LaTeX boilerplate %%%%%%%%%%%%%%%%%%%%%%%%%%%
2 % we have our own preamble,
3 % use `noweave -delay` to not print noweb's automatic one
4 % -*- mode: Noweb; noweb-code-mode: c-mode -*-
5 \documentclass[letterpaper, 11pt]{article}
8 \noweboptions{smallcode}
10 \usepackage{url} % needed for natbib for underscores in urls?
11 \usepackage[breaklinks,colorlinks]{hyperref} % for internal links, \phantomsection
12 % breaklinks breaks long links
13 % colorlinks colors link text instead of boxing it
14 \usepackage{amsmath} % for \text{}, \xrightarrow[sub]{super}
15 \usepackage{amsthm} % for theorem and proof environments
16 \newtheorem{theorem}{Theorem}
18 \usepackage{doc} % BibTeX
19 \usepackage[super,sort&compress]{natbib} % fancy citation extensions
20 % super selects citations in superscript mode
21 % sort&compress automatically sorts and compresses compound citations (\cite{a,b,...})
23 \usepackage[mathletters]{ucs} % use math fonts to allow Greek UTF-8 in the text
24 \usepackage[utf8x]{inputenc} % allow UTF-8 characters
26 %\bibliographystyle{ieeetr} % pick the bibliography style, short and sweet
27 %\bibliographystyle{plain} % pick the bibliography style, includes dates
28 \bibliographystyle{plainnat}
29 \defcitealias{sw:noweb}{{\tt noweb}}
30 \defcitealias{galassi05}{GSL}
31 \defcitealias{sw:check}{{\tt check}}
32 \defcitealias{sw:python}{Python}
37 \textheight 9.5in % leave a bit of extra room for page numbers (at top of page)
40 \setlength{\parindent}{0pt}
41 \setlength{\parskip}{5pt}
43 % For some reason, the \maketitle wants to go on it's own page
44 % so we'll just hardcode the following in our first page.
45 %\title{Sawsim: a sawtooth protein unfolding simulator}
46 %\author{W.~Trevor King}
49 \newcommand{\prog}{[[sawsim]]}
50 \newcommand{\lang}{[[C]]}
51 \newcommand{\p}[3]{\left#1 #2 \right#3} % e.g. \p({complicated expr})
52 \newcommand{\Th}{\ensuremath{\sup{\text{th}}}} % ordinal th
53 \newcommand{\U}[1]{\ensuremath{\text{ #1}}} % units: $1\U{m/s}$
55 % single spaced lists, from Alvin Alexander
56 % http://www.devdaily.com/blog/post/latex/control-line-spacing-in-itemize-enumerate-tags/
57 \newenvironment{packed_item}{
59 \setlength{\itemsep}{1pt}
60 \setlength{\parskip}{0pt}
61 \setlength{\parsep}{0pt}
65 \nwfilename{sawsim.nw}
70 Sawsim: a sawtooth protein unfolding simulator \\
75 %%%%%%%%%%%%%%%%%%%%%%%%%%%% End LaTeX boilerplate %%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 \section{Introduction}
79 The unfolding of multi-globular protein strings is a stochastic
80 process. In the \prog\ program, we use Monte Carlo methods to
81 simulate this unfolding through an explicit time-stepping approach.
82 We develop a program in \lang\ to simulate probability of unfolding
83 under a constant extension velocity of a chain of globular domains.
85 In order to extract physical meaning from many mechanical unfolding
86 simulations, it is necessary to compare the experimental results
87 against the results of simulations using different models and
88 parameters. The model and parameters that best match the experimental
89 data provide the ‘best’ model for that experiment.
91 Previous mechanical unfolding studies have rolled their own
92 simulations for modelling their experiment, and there has been little
93 exchange and standardization of these simulations between groups.
94 The \prog\ program attempts to fill this gap, providing a flexible,
95 extensible, \emph{community} program, to make it easier for groups
96 to share the burden and increase the transparency of data analysis.
98 ‘Sawsim’ is blend of ‘sawtooth simulation’.
100 \subsection{Related work}
104 \subsection{About this document}
106 This document was written in \citetalias{sw:noweb} with code blocks in
107 \lang\ and comment blocks in \LaTeX.
109 \subsection{System Requirements and Dependencies}
112 \subsection{Change Log}
114 Version 0.0 used only the unfolded domain WLC to determine the tension
115 and had a fixed timestep $dt$.
117 Version 0.1 added adaptive timesteps, adjusting so that the unfolding
118 probability for a given domain was constant.
120 Version 0.2 added Kramers' model unfolding rate calculations, and a
121 switch to select Bell's or Kramers' model. This induced a major
122 rewrite, introducing the tension group abstraction, and a split to
123 multiple \lang\ source files. Also added a unit-test suites for
124 sanity checking, and switched to SI units throughout.
126 Version 0.3 added integrated Kramers' model unfolding rate
127 calculations. Also replaced some of my hand-coded routines with GNU
128 Scientific Library \citepalias{galassi05} calls.
130 Version 0.4 added Python evaluation for the saddle-point Kramers'
131 model unfolding rate calculations, so that the model functions could
132 be controlled from the command line. Also added interpolating lookup
133 tables to accelerate slow unfolding rate model evaluation, and command
134 line control of tension groups.
136 Version 0.5 added the stiffness environmental parameter and it's
137 associated unfolding models.
139 <<version definition>>=
140 #define VERSION "0.5"
146 sawsim - program for simulating single molecule mechanical unfolding.
147 Copyright (C) 2008, William Trevor King
149 This program is free software; you can redistribute it and/or
150 modify it under the terms of the GNU General Public License as
151 published by the Free Software Foundation; either version 3 of the
152 License, or (at your option) any later version.
154 This program is distributed in the hope that it will be useful, but
155 WITHOUT ANY WARRANTY; without even the implied warranty of
156 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
157 See the GNU General Public License for more details.
159 You should have received a copy of the GNU General Public License
160 along with this program; if not, write to the Free Software
161 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
164 The author may be contacted at <wking@drexel.edu> on the Internet, or
165 write to Trevor King, Drexel University, Physics Dept., 3141 Chestnut St.,
166 Philadelphia PA 19104, USA.
178 The unfolding system is stored as a chain of \emph{domains}. Domains
179 can be folded, globular protein domains, unfolded protein linkers, AFM
180 cantilevers, or other stretchable system components.
182 Each domain contributes to the total tension, which depends on the
183 chain extension. The particular model for the tension as a function
184 of extension is handled generally with function pointers. So far the
185 following models have been implemented:
187 \item Null (Appendix \ref{sec.null_tension}),
188 \item Constant (Appendix \ref{sec.const_tension}),
189 \item Hookean spring (Appendix \ref{sec.hooke}),
190 \item Worm-like chain (WLC, Appendix \ref{sec.wlc}), and
191 \item Freely-jointed chain (FJC, Appendix \ref{sec.fjc}).
194 The tension model and parameters used for a particular domain depend
195 on whether the domain is folded or unfolded. The transition rate from
196 the folded state to the unfolded state is also handled generally with
197 function pointers, with implementations for
199 \item Null (Appendix \ref{sec.null_k}),
200 \item Bell's model (Appendix \ref{sec.bell}),
201 \item Spring constant corrected Bell model (Appendix \ref{sec.kbell}),
202 \item Kramers' integral model (Appendix \ref{sec.kramers_integ}), and
203 \item Kramers' saddle point model (Appendix \ref{sec.kramers}).
206 The unfolding of the chain domains is modeled in two stages. First
207 the tension of the chain is calculated. Then the tension (assumed to
208 be constant over the length of the chain), is applied to each folded
209 domain, and used to calculate the probability of that subdomain
210 unfolding in the next timestep $dt$. Then the time is stepped
211 forward, and the process is repeated.
214 int main(int argc, char **argv)
216 <<tension model getopt array definition>>
217 <<k model getopt array definition>>
218 list_t *domain_list=NULL; /* variables initialized in get_options() */
219 environment_t env={0};
220 double P, dt_max, v, xstep;
221 int num_folded=0, unfolding;
222 double x=0, dt, F; /* variables used in the simulation loop */
223 get_options(argc, argv, &P, &dt_max, &v, &xstep, &env, NUM_TENSION_MODELS,
224 tension_models, NUM_K_MODELS, k_models, &domain_list, &num_folded);
226 if (flags & FLAG_OUTPUT_FULL_CURVE) printf("#Distance (m)\tForce (N)\tStiffness (N/m)\n");
227 if (flags & FLAG_OUTPUT_UNFOLDING_FORCES) printf("#Unfolding Force (N)\n");
228 while (num_folded > 0) {
229 F = find_tension(NUM_TENSION_MODELS, tension_handlers, domain_list, &env, x, unfolding);
231 dt = determine_dt(NUM_TENSION_MODELS, tension_handlers, domain_list, &env, x, P, dt_max, v);
233 dt = determine_dt(NUM_TENSION_MODELS, tension_handlers, domain_list, &env, x, P, dt_max, 0);
234 unfolding=random_unfoldings(domain_list, F, dt, &env, &num_folded);
235 if (flags & FLAG_OUTPUT_FULL_CURVE) printf("%g\t%g\t%g\n", x, F, env.stiffness);
239 if (dt == dt_max) { /* step completed */
242 } else { /* still working on this step */
247 destroy_domain_list(domain_list);
251 @ The meat of the simulation is bundled into the three functions
252 [[find_tension]], [[determine_dt]], and [[random_unfoldings]].
253 [[find_tension]] is discussed in Section \ref{sec.find_tension},
254 [[determine_dt]] in Section \ref{sec.adaptive_dt}, and
255 [[random_unfoldings]] in Section \ref{sec.unfolding_rate}.
257 The stretched distance is extended in one of two modes depending on
258 [[xstep]]. If [[xstep == 0]], then the pulling is continuous, at
259 least within the limits of the inherent discretization of a time
260 stepped approach. At any rate, the timestep is limited by the maximum
261 allowed timestep [[dt_max]] and the maximum allowed unfolding
262 probability in a given step [[P]]. In the second mode [[xstep > 0]],
263 and the end to end distance increases in discrete steps of that
264 length. The time between steps is chosen to maintain an average
265 unfolding velocity [[v]]. This approach is less work to simulate than
266 smooth pulling and also closer to the reality of many experiments, but
267 it is practically impossible to treat analytically. With both pulling
268 styles implemented, the effects of the discretization can be easily
269 calculated, bridging the gap between theory and experiment.
271 Environmental parameters important in determining reaction rates and
272 tensions (e.g. temperature, pH) are stored in a single structure to
273 facilitate extension to more complicated models in the future.
274 <<environment definition>>=
275 typedef struct environment_struct {
284 <<environment definition>>
285 <<one dimensional function definition>>
286 <<create/destroy definitions>>
288 #endif /* GLOBAL_H */
290 Where the [[GLOBAL_H]] defines are protecting [[global.h]] from being
291 included multiple times.
293 \section{Simulation functions}
295 <<simulation functions>>=
299 <<does domain unfold>>
300 <<randomly unfold domains>>
304 \label{sec.find_tension}
306 Because the stretched system may be made up of several parts (folded
307 domains, unfolded domains, spring-like cantilever, \ldots), we will
308 assign the domains to models and groups. The models are used to
309 determine a domain's tension (Hookean spring, WLC, \ldots). Groups
310 will represent collections of domains which the model should treat as
311 a single entity. A domain may belong to different groups or models
312 depending on its state. For example, a domain might be modeled as a
313 freely-jointed chain when it is folded and as a worm-like chain when
314 it is unfolded. The domains are assumed to be commutative, so
315 ordering is ignored. The interactions between the groups are assumed
316 to be linear, but the interactions between domains of the same group
317 need not be. This allows for non-linear group models such as th
318 worm-like or freely-jointed chains. Each model has a tension handler
319 function, which gives the tension $F$ needed to stretch a given group
320 of domains a distance $x$.
322 To understand the purpose of groups, consider a chain of proteins
323 where two folded proteins $A$ and $B$ are modeled as WLCs with
324 persistence length $p_f$ and two unfolded proteins $C$ and $D$ are
325 modeled as WLCs with persistence length $p_u$. The proteins are
326 attached to a cantilever $E$ qof spring constant $κ$. The string
327 would get split into three lists:
329 \begin{tabular}{llll}
330 Model & Group & List & Tension \\
331 WLC & 0 & $[A,B]$ & $F_\text{WLC}(x, p_f, L_A+L_B)$ \\
332 WLC & 1 & $[C,D]$ & $F_\text{WLC}(x, p_u, L_C+L_D)$ \\
333 Hooke & 0 & $[E]$ & $F_\text{Hooke}(x, κ)$ \\
336 Note that group numbers only matter \emph{within} model classes, since
337 grouping domains with seperate models doesn't make sense.
339 <<find tension definitions>>=
340 #define NUM_TENSION_MODELS 5
344 <<tension handler array global declaration>>=
345 extern one_dim_fn_t *tension_handlers[];
348 <<tension handler array global>>=
349 one_dim_fn_t *tension_handlers[] = {
351 &const_tension_handler,
359 <<tension model global declarations>>=
360 <<tension handler array global declaration>>
363 <<tension handler types>>=
364 typedef struct tension_handler_data_struct {
365 /* int num_domains; */
366 /* domain_t *domains; */
370 } tension_handler_data_t;
374 After sorting the chain into separate groups $G_i$, with tension
375 handlers $F_i(G_i; x_i)$, we need to balance the extension $x_i$ of
376 each group to get a consistent tension $F_i(x_i) = F_j(x_j) \;\;
377 \forall i,j$. Note that there may be several groups within each model
378 class. [[find_tension]] is basically a wrapper to feed proper input
379 into the [[tension_balance]] function. [[unfolding]] should be set to
380 0 if there were no unfoldings since the previous call to
381 [[find_tension]]. While were messing with the tension handlers, we
382 also grab a measure of the current linker stiffness for the
383 environmental variable, which is needed by some of the unfolding rate
384 models (e.g. the linker-stiffness corrected Bell model, Appendix
387 double find_tension(int num_tension_handlers, one_dim_fn_t **tension_handlers, list_t *domain_list, environment_t *env, double x, int unfolding)
389 static int num_active_groups=0;
390 static one_dim_fn_t **per_group_handlers = NULL;
391 static void **per_group_params = NULL;
392 static double last_x;
393 tension_handler_data_t *tdata;
398 fprintf(stderr, "finding tension at distance %g%s\n", x, unfolding ? " after an unfolding" : "");
399 list_print(stderr, domain_list, "domain list");
402 if (unfolding != 0 || num_active_groups == 0) { /* setup statics */
403 /* free old statics */
404 if (per_group_handlers != NULL)
405 free(per_group_handlers);
406 if (per_group_params != NULL) {
407 for (i=0; i < num_active_groups; i++) {
408 assert(per_group_params[i] != NULL);
409 free(per_group_params[i]);
411 free(per_group_params);
414 /* get new statics */
415 get_active_groups(domain_list, num_tension_handlers, tension_handlers, &num_active_groups, &per_group_handlers, &per_group_params);
417 /* fill in the group handlers and params */
418 for (i=0; i<num_active_groups; i++) {
419 tdata = (tension_handler_data_t *) per_group_params[i];
421 fprintf(stderr, "active group %d of %d", i, num_active_groups);
422 list_print(stderr, tdata->group, " parameters");
425 /* tdata->persist continues unchanged */
428 } /* else roll with the current statics */
430 F = tension_balance(num_active_groups, per_group_handlers, (void **)per_group_params, last_x, x);
437 fprintf(stderr, "finding linker stiffness at distance %g, tension %g\n", x, F);
443 if (dx == 0) { /* e.g. if x == 0 */
447 Flow = tension_balance(num_active_groups, per_group_handlers, (void **)per_group_params, last_x, xlow);
448 env->stiffness = (F-Flow)/dx;
451 fprintf(stderr, " stiffness (%g-%g)/%g = %g\t(dx = %g = %g-%g)\n", F, Flow, dx, env->stiffness, dx, x, xlow);
459 @ For the moment, we will restrict our group boundaries to a single
460 dimension, so $\sum_i x_i = x$, our total extension (it is this
461 restriction that causes the groups to interact linearly). We'll also
462 restrict our extensions to all be positive. With these restrictions,
463 the problem of balancing the tensions reduces to solving a system of
464 $N+1$ possibly non-linear equations with $N$ unknowns, where $N$ is
465 the number of active groups. In general this can be fairly
466 complicated, but without much loss of practicality we can restrict
467 ourselves to strictly monotonically increasing, non-negative tension
468 functions $F_i(x)$, with $F_i(0)=0$, which makes the situation much
469 simpler. For example, it guarantees the existence of a unique, real
470 solution for finite forces. See Appendix \ref{app.tension_balance}
471 for the tension balancing implementation.
473 Each group must define a parameter structure if the tension model is
475 a function to create the parameter structure,
476 a function to destroy the parameter structure, and
477 a tension function of type [[one_dim_fn_t]] that receives a [[tension_handler_data_t *]] as it's data argument.
478 The tension-specific parameter structures are contained in the domain
479 group field. See the Section \ref{app.model_selection} for a
480 discussion on the command line framework. See the worm-like chain
481 implementation for an example (Section \ref{sec.wlc}).
483 The major limitation of our approach is that all of our tension models
484 are Markovian (memory-less), which is not really a problem for the
485 chains (see \citet{evans99} for WLC thermalization rate calculations).
487 \subsection{Unfolding rate}
488 \label{sec.unfolding_rate}
490 Each folded domain is modeled as unimolecular, first order reaction
492 \text{Folded} \xrightarrow{k} % in tex: X atop Y
495 With the rate constant $k$ defined by
497 \frac{d[\text{Folded}]}{dt} = -k [\text{Folded}].
499 So the probability of a given protein unfolding in a short time $dt$
505 <<does domain unfold>>=
506 int domain_unfolds(double F, double dt, environment_t *env, domain_t *domain)
507 { /* returns 1 or 0, F in N, dt in s, pointer to env. data, pointer to a folded domain */
509 k = accel_k(domain->k, F, env, domain->k_params);
510 //(*domain->k)(F, env, domain->k_params);
511 //printf("k = %g,\tdt = %g,\tk dt = %g\n", k, dt, k*dt);
512 return happens(k*dt); /* dice roll for prob. k*dt event */
514 @ [[happens]] is a random decision making function defined in Appendix \ref{app.utils}.
516 Only one domain can unfold in each timestep, because the timescale of
517 a domain unfolding $dt_u$ is assumed to be less than the simulation
518 timestep $dt$, so a domain will completely unfold in a single
519 timestep. We adapt our timesteps to keep the probability of a single
520 domain unfolding low, so the probability of two domains unfolding in
521 the same timestep is negligible. This approach breaks down as the
522 adaptive timestep scheme approaches $dt \sim dt_u$, but $dt_u \sim
523 1\U{$\mu$s}$ for Ig27-like proteins \citep{klimov00}, so this
524 shouldn't be a problem. To reassure yourself, you can ask the
525 simulator to print the smallest timestep that was used with TODO.
526 <<randomly unfold domains>>=
527 int random_unfoldings(list_t *domain_list, double tension,
528 double dt, environment_t *env,
529 int *num_folded_domains)
530 { /* returns 1 if there was an unfolding and 0 otherwise */
531 while (domain_list != NULL) {
532 if (D(domain_list)->state == DS_FOLDED
533 && domain_unfolds(tension, dt, env, D(domain_list))) {
534 if (flags & FLAG_OUTPUT_UNFOLDING_FORCES)
535 fprintf(stdout, "%g\n", tension);
536 D(domain_list)->state = DS_UNFOLDED;
537 (*num_folded_domains)--;
538 return 1; /* our one domain has unfolded, stop looking for others */
540 domain_list = domain_list->next;
546 \subsection{Adaptive timesteps}
547 \label{sec.adaptive_dt}
549 We'd like to pick $dt$ so the probability of unfolding in the next
550 timestep is small. If we don't adapt our timestep, we risk breaking
551 our approximation that $F(x' \in [x, x+v \cdot dt]) = F(x)$ or that
552 only one domain unfolds in a given timestep. Because $F(x)$ is
553 monotonically increasing, excessively large timesteps will lead to
554 erroneously large unfolding forces. The simulation would have been
555 accurate for sufficiently small fixed timesteps, but adaptive
556 timesteps will allow us to move through low tension regions in fewer
557 steps, leading to a more efficient simulation.
559 The actual adaptive timestep implementation is not particularly
560 interesting, since we are only required to reduce $dt$ to somewhere
561 below a set threshold, so I've removed it to Appendix
562 \ref{app.adaptive_dt}.
568 The models provide the physics of the simulation, but the simulation
569 allows interchangeable models, and we are currently focusing on the
570 simulation itself, so we remove the actual model implementation to the
573 The tension models are defined in Appendix \ref{sec.tension_models}
574 and unfolding models are defined in Appendix \ref{sec.k_models}.
577 #define k_B 1.3806503e-23 /* J/K */
581 \section{Command line interface}
583 <<get option functions>>=
585 <<index tension model>>
591 \subsection{Model selection}
592 \label{app.model_selection}
594 The main difficulty with the command line interface in \prog\ is
595 developing an intuitive method for accessing the possibly large number
596 of available models. We'll treat this generally by defining an array
597 of available models, containing their important parameters.
599 <<get options definitions>>=
600 <<model getopt definitions>>
603 <<create/destroy definitions>>=
604 typedef void *create_data_func_t(char **param_strings);
605 typedef void destroy_data_func_t(void *param_struct);
608 <<model getopt definitions>>=
609 <<tension model getopt definitions>>
610 <<k model getopt definitions>>
614 \subsubsection{Tension}
616 To access the various tension models from the command line, we define
617 a structure that contains all the neccessary information about the
619 <<tension model getopt definitions>>=
620 typedef struct tension_model_getopt_struct {
621 one_dim_fn_t *handler; /* fn implementing the model on a group */
622 char *name; /* name string identifying the model */
623 char *description; /* a brief description of the model */
624 int num_params; /* number of extra params for the model */
625 char **param_descriptions; /* descriptions of extra params */
626 char *params; /* default values for the extra params */
627 create_data_func_t *creator; /* param-string -> model-data-struct fn */
628 destroy_data_func_t *destructor; /* fn to free the model data structure */
629 } tension_model_getopt_t;
632 <<tension model getopt array definition>>=
633 tension_model_getopt_t tension_models[NUM_TENSION_MODELS] = {
634 <<null tension model getopt>>,
635 <<constant tension model getopt>>,
636 <<hooke tension model getopt>>,
637 <<worm-like chain tension model getopt>>,
638 <<freely-jointed chain tension model getopt>>
642 \subsubsection{Unfolding rate}
644 <<k model getopt definitions>>=
645 #define NUM_K_MODELS 6
647 typedef struct k_model_getopt_struct {
652 char **param_descriptions;
654 create_data_func_t *creator;
655 destroy_data_func_t *destructor;
659 <<k model getopt array definition>>=
660 k_model_getopt_t k_models[NUM_K_MODELS] = {
661 <<null k model getopt>>,
662 <<const k model getopt>>,
663 <<bell k model getopt>>,
664 <<kbell k model getopt>>,
665 <<kramers k model getopt>>,
666 <<kramers integ k model getopt>>
673 void help(char *prog_name, double P, double dt_max, double v, double xstep,
675 int n_tension_models, tension_model_getopt_t *tension_models,
676 int folded_tension_model, int unfolded_tension_model,
677 int n_k_models, k_model_getopt_t *k_models,
681 printf("usage: %s [options]\n", prog_name);
682 printf("Version %s\n\n", VERSION);
683 printf("Monte Carlo simulation of a multi-globular domain protein unfolding\n\n");
684 printf("Simulation options:\n");
685 printf("-P P\tTarget probability for dt (currently %g)\n", P);
686 printf("-t dt\tMaximum allowed timestep dt (currently %g)\n", dt_max);
687 printf("-v v\tPulling velocity v (currently %g nm/s)\n", v);
688 printf("-x dx\tPulling step size (currently %g m)\n", xstep);
689 printf("\tWhen dx = 0, the pulling is continuous\n");
690 printf("\tWhen dx > 0, the pulling occurs in discrete steps\n");
691 printf("Environmental options:\n");
692 printf("-T T\tTemperature T (currently %g K)\n", env->T);
693 printf("-C T\tYou can also set the temperature T in Celsius\n");
694 printf("Model options:\n");
695 printf("The domains exist in either the folded or unfolded state\n");
696 printf("The following options change the default behavior in each state.\n");
697 printf("-m model[,group]\tFolded tension model (currently %s)\n",
698 tension_models[folded_tension_model].name);
699 printf("-a args\tFolded tension model argument string (currently %s)\n",
700 tension_models[folded_tension_model].params);
701 printf("-M model[,group]\tUnfolded tension model (currently %s)\n",
702 tension_models[unfolded_tension_model].name);
703 printf("-A args\tUnfolded tension model argument string (currently %s)\n",
704 tension_models[unfolded_tension_model].params);
705 printf("The following options change the unfolding rate.\n");
706 printf("-k model\tTransition rate model (currently %s)\n",
707 k_models[k_model].name);
708 printf("-K args\tTransition rate model argument string (currently %s)\n",
709 k_models[k_model].params);
710 printf("Domain creation options:\n");
711 printf("Once you've set up the appropriate default models, you need to add the domains\n");
712 printf("-F n\tAdd n folded domains with the current models\n");
713 printf("-U n\tAdd n unfolded domains with the current models\n");
714 printf("Output mode options:\n");
715 printf("There are two output modes. In standard mode, only the unfolding\n");
716 printf("events are printed. For example:\n");
717 printf(" #Unfolding Force (N)\n");
718 printf(" 123.456e-12\n");
720 printf("In verbose mode, the entire Force-vs-distance curve is output:\n");
721 printf(" #Distance (m)\tForce (N)\tStiffness (N/m)\n");
722 printf(" 0.001\t0.0023\n");
724 printf("-V\tChange output to verbose mode\n");
725 printf("-h\tPrint this help and exit\n");
727 printf("Tension models:\n");
728 for (i=0; i<n_tension_models; i++) {
729 printf("\t%s\t%s\n", tension_models[i].name, tension_models[i].description);
730 for (j=0; j < tension_models[i].num_params ; j++)
731 printf("\t\t\t%s\n", tension_models[i].param_descriptions[j]);
732 printf("\t\tdefaults: %s\n", tension_models[i].params);
734 printf("Unfolding rate models:\n");
735 for (i=0; i<n_k_models; i++) {
736 printf("\t%s\t%s\n", k_models[i].name, k_models[i].description);
737 for (j=0; j < k_models[i].num_params ; j++)
738 printf("\t\t\t%s\n", k_models[i].param_descriptions[j]);
739 printf("\t\tdefaults: %s\n", k_models[i].params);
741 printf("Examples:\n");
742 printf("1 spring and 8 folded domains of titin I27 only stretchable when unfolded\n");
743 printf(" $ %s -v1e-6 -Mhooke -A.05 -U1 -kbell -K3.3e-4,.25e-9 -mnull -Mwlc -A0.39e-9,28e-9 -F8\n", prog_name);
744 printf("1 spring and 8 folded domains of ubiquitin with different WLC parameters when folded or unfolded\n");
745 printf(" $ %s -v1e-6 -Mhooke -A.05 -U1 -kbell -K5e-5,.225e-9 -mwlc -a1e-8,4e-10 -Mwlc,1 -A0.4e-9,28.1e-9 -F8\n", prog_name);
749 \subsection{Get options}
752 void get_options(int argc, char **argv,
753 double *pP, double *pDt_max, double *pV, double *pXstep,
755 int n_tension_models, tension_model_getopt_t *tension_models,
756 int n_k_models, k_model_getopt_t *k_models,
757 list_t **pDomain_list, int *pNum_folded)
759 char *prog_name = NULL;
760 char c, options[] = "P:t:v:x:T:C:m:a:M:A:k:K:F:U:Vh";
761 int ftension_model=0, utension_model=0, k_model=0;
762 char *ftension_params=NULL, *utension_params=NULL;
763 int i, n, ftension_group=0, utension_group=0;
767 extern int optind, optopt, opterr;
772 for (i=0; i<n_tension_models; i++) {
773 fprintf(stderr, "tension model %i %s:\t model's handler %p\n",
774 i, tension_models[i].name, tension_models[i].handler);
775 assert(tension_models[i].handler == tension_handlers[i]);
780 flags = FLAG_OUTPUT_UNFOLDING_FORCES;
782 *pP = 1e-3; /* % pop per s */
783 *pDt_max = 0.001; /* s */
784 *pV = 1e-6; /* m/s */
785 *pXstep = 0.0; /* m */
786 env->T = 300.0; /* K */
788 while ((c=getopt(argc, argv, options)) != -1) {
790 case 'P': *pP = atof(optarg); break;
791 case 't': *pDt_max = atof(optarg); break;
792 case 'v': *pV = atof(optarg); break;
793 case 'x': *pXstep = atof(optarg); break;
794 case 'T': env->T = atof(optarg); break;
795 case 'C': env->T = atof(optarg)+273.15; break;
797 parse_list_string(optarg, SEP, '{', '}', &num_strings, &strings);
798 assert(num_strings == 1 || num_strings == 2);
799 if (num_strings == 1)
802 ftension_group = atoi(strings[1]);
803 ftension_model = index_tension_model(n_tension_models, tension_models, strings[0]);
804 ftension_params = tension_models[ftension_model].params;
805 free_parsed_list(num_strings, strings);
808 ftension_params = optarg;
811 parse_list_string(optarg, SEP, '{', '}', &num_strings, &strings);
812 assert(num_strings == 1 || num_strings == 2);
813 if (num_strings == 1)
816 utension_group = atoi(strings[1]);
817 utension_model = index_tension_model(n_tension_models, tension_models, strings[0]);
818 utension_params = tension_models[utension_model].params;
819 free_parsed_list(num_strings, strings);
822 utension_params = optarg;
825 k_model = index_k_model(n_k_models, k_models, optarg);
828 k_models[k_model].params = optarg;
833 for (i=0; i<n; i++) {
834 push(pDomain_list, generate_domain(DS_FOLDED,
835 tension_models+ftension_model,
838 tension_models+utension_model,
841 k_models+k_model), 1);
848 for (i=0; i<n; i++) {
849 push(pDomain_list, generate_domain(DS_UNFOLDED,
850 tension_models+ftension_model,
853 tension_models+utension_model,
856 k_models+k_model), 1);
860 flags = FLAG_OUTPUT_FULL_CURVE;
863 fprintf(stderr, "unrecognized option '%c'\n", optopt);
864 /* fall through to default case */
866 help(prog_name, *pP, *pDt_max, *pV, *pXstep, env, n_tension_models, tension_models, ftension_model, utension_model, n_k_models, k_models, k_model);
870 /* check the arguments */
871 assert(*pDomain_list != NULL); /* no domains! */
872 assert(*pP > 0.0); assert(*pP < 1.0);
873 assert(*pDt_max > 0.0);
875 assert(env->T > 0.0);
880 <<index tension model>>=
881 int index_tension_model(int n_models, tension_model_getopt_t *models, char *name)
884 for (i=0; i<n_models; i++)
885 if (STRMATCH(models[i].name, name))
888 fprintf(stderr, "Unrecognized tension model '%s'\n", name);
896 int index_k_model(int n_models, k_model_getopt_t *models, char *name)
899 for (i=0; i<n_models; i++)
900 if (STRMATCH(models[i].name, name))
903 fprintf(stderr, "Unrecognized unfolding rate model '%s'\n", name);
910 <<initialize model definition>>=
911 /* requires int num_param_args and char **param_args in the current scope
913 * INIT_MODEL("folded", folded_model, folded_param_string, folded_params);
914 * defined as a macro, so it can work on both tension_model_getopt_t and k_model_getopt_t types.
916 #define INIT_MODEL(role, model, param_string, param_pointer) \
918 parse_list_string(param_string, SEP, '{', '}', \
919 &num_param_args, ¶m_args); \
920 if (num_param_args != model->num_params) { \
922 "%s model %s expected %d params,\n", \
923 role, model->name, model->num_params); \
925 "not the %d params in '%s'\n", \
926 num_param_args, param_string); \
927 assert(num_param_args == model->num_params); \
929 if (model->creator) \
930 param_pointer = (*model->creator)(param_args); \
932 param_pointer = NULL; \
933 free_parsed_list(num_param_args, param_args); \
938 void *generate_domain(enum domain_state_t state,
939 tension_model_getopt_t *folded_model,
941 char *folded_param_string,
942 tension_model_getopt_t *unfolded_model,
944 char *unfolded_param_string,
945 k_model_getopt_t *k_model)
947 void *folded_params, *unfolded_params, *k_params;
948 int num_param_args; /* for INIT_MODEL() */
949 char **param_args; /* for INIT_MODEL() */
952 fprintf(stderr, "generating %s ", state==DS_FOLDED ? "folded" : "unfolded");
953 fprintf(stderr, "domain (k: \"%s\", \"%s\", f: \"%s\" (%p) group %d \"%s\", u: \"%s\" (%p) group %d \"%s\")\n",
954 k_model->name, k_model->params,
955 folded_model->name, folded_model->handler, folded_group, folded_param_string,
956 unfolded_model->name, unfolded_model->handler, unfolded_group, unfolded_param_string);
958 INIT_MODEL("folded", folded_model, folded_param_string, folded_params);
959 INIT_MODEL("unfolded", unfolded_model, unfolded_param_string, unfolded_params);
960 INIT_MODEL("k", k_model, k_model->params, k_params);
961 return create_domain(state,
962 k_model->k, k_params, k_model->destructor,
963 folded_model->handler, folded_group, folded_params, folded_model->destructor,
964 unfolded_model->handler, unfolded_group, unfolded_params, unfolded_model->destructor
971 \addcontentsline{toc}{section}{Appendicies}
973 \section{sawsim.c details}
977 The general layout of our simulation code is:
987 We include [[math.h]], so don't forget to link to the libm with `-lm'.
989 #include <assert.h> /* assert() */
990 #include <stdlib.h> /* malloc(), free(), rand() */
991 #include <stdio.h> /* fprintf(), stdout */
992 #include <string.h> /* strlen, strtok() */
993 #include <math.h> /* exp(), M_PI, sqrt() */
994 #include <sys/types.h> /* pid_t (returned by getpid()) */
995 #include <unistd.h> /* getpid() (for seeding rand()), getopt() */
998 #include "tension_balance.h"
1000 #include "tension_model.h"
1002 #include "accel_k.h"
1006 <<version definition>>
1007 <<flag definitions>>
1008 <<max/min definitions>>
1009 <<string comparison definition and globals>>
1010 <<initialize model definition>>
1011 <<get options definitions>>
1012 <<domain definitions>>
1021 <<create/destroy domain>>
1022 <<destroy domain list>>
1024 <<simulation functions>>
1025 <<boilerplate functions>>
1028 <<boilerplate functions>>=
1030 <<get option functions>>
1036 srand(getpid()*time(NULL)); /* seed rand() */
1040 <<flag definitions>>=
1041 /* in octal b/c of prefixed '0' */
1042 #define FLAG_OUTPUT_FULL_CURVE 01
1043 #define FLAG_OUTPUT_UNFOLDING_FORCES 02
1047 static unsigned long int flags = 0;
1050 \subsection{Utilities}
1053 <<max/min definitions>>=
1054 #define MAX(a,b) ((a)>(b) ? (a) : (b))
1055 #define MIN(a,b) ((a)<(b) ? (a) : (b))
1058 Note that [[STRMATCH]] chokes if one of the strings is [[NULL]].
1059 <<string comparison definition and globals>>=
1060 // Check if two strings match, return 1 if they do
1061 static char *temp_string_A;
1062 static char *temp_string_B;
1063 #define STRMATCH(a,b) (temp_string_A=a, temp_string_B=b, \
1064 strlen(temp_string_A) != strlen(temp_string_B) ? 0 : \
1065 !strncmp(temp_string_A,temp_string_B,strlen(temp_string_B)+1) )
1066 /* +1 to also compare the '\0' */
1069 We also define a macro for our [[check]] unit testing
1070 <<check relative error>>=
1071 #define CHECK_ERR(max_err, expected, received) \
1073 fail_unless( (received-expected)/expected < max_err, \
1074 "relative error %g >= %g in %s (Expected %g, received %g)", \
1075 (received-expected)/expected, max_err, #received, \
1076 expected, received); \
1077 fail_unless(-(received-expected)/expected < max_err, \
1078 "relative error %g >= %g in %s (Expected %g, received %g)", \
1079 -(received-expected)/expected, max_err, #received, \
1080 expected, received); \
1085 int happens(double probability)
1087 assert(probability >= 0.0); assert(probability <= 1.0);
1088 return (double)rand()/RAND_MAX < probability; /* TODO: replace with GSL rand http://www.gnu.org/software/gsl/manual/html_node/Random-number-generator-algorithms.html x*/
1093 \subsection{Adaptive timesteps}
1094 \label{app.adaptive_dt}
1096 $F(x)$ increases with $x$, possibly exploding, as in the worm-like chain model,
1097 so basing the timestep on the the unfolding probability at the current tension
1098 is dangerous, and we need to search for a $dt$ for which
1099 $P(F(x+v*dt)) < P_\text{target}$.
1100 There are two cases to consider.
1101 In the most common, no domains have unfolded since the last step,
1102 and we expect the next step to be slightly shorter than the previous one.
1103 In the less common, domains did unfold in the last step,
1104 and we expect the next step to be considerably longer than the previous one.
1106 double search_dt(int num_tension_handlers, one_dim_fn_t **tension_handlers,
1107 list_t *domain_list,
1108 environment_t *env, double x,
1109 double target_prob, double max_dt, double v)
1110 { /* Returns the timestep dt in seconds for the current folded domain.
1111 * Takes a list of tension handlers, the list of domains,
1112 * a pointer env to the environmental data, a starting separation x in m,
1113 * a target_prob between 0 and 1,
1114 * max_dt in s, stretching velocity v in m/s.
1116 double F, k, dtCur, dtU, dtUCur, dtL, dt;
1118 /* get upper bound using the current position */
1119 F = find_tension(num_tension_handlers, tension_handlers, domain_list, env, x, 0); /* BUG. repeated calculation */
1120 //printf("Start with x = %g (F = %g)\n", x, F);
1121 k = accel_k(D(domain_list)->k, F, env, D(domain_list)->k_params);
1122 //printf("x %g\tF %g\tk %g\n", x, F, k);
1123 dtU = target_prob / k; /* P = k dt, dtU is an upper bound on dt */
1125 //printf("overshot max_dt\n");
1128 if (v == 0) /* discrete stepping, so force is temporatily constant */
1131 /* set a lower bound on dt too */
1134 /* The dt determined above may produce illegitimate forces or ks.
1135 * Reduce the upper bound until we have valid ks. */
1137 F = find_tension(num_tension_handlers, tension_handlers, domain_list, env, x+v*dt, 0);
1138 while (F == HUGE_VAL) { /* reduce step until we hit a valid force */
1141 F = find_tension(num_tension_handlers, tension_handlers, domain_list, env, x+v*dt, 0);
1143 //printf("Try for dt = %g (F = %g)\n", dt, F);
1144 k = accel_k(D(domain_list)->k, F, env, D(domain_list)->k_params);
1145 /* returned k may be -1.0 */
1146 //printf("x %g\tF %g\tdt %g\tv dt %g\tk %g\n", x, F, dt, v*dt, k);
1147 while (k == -1.0) { /* reduce step until we hit a valid k */
1149 dt = dtU; /* hopefully, we can use the max dt, see if it works */
1150 F = find_tension(num_tension_handlers, tension_handlers, domain_list, env, x+v*dt, 0);
1151 //printf("Try for dt = %g (F = %g)\n", dt, F);
1152 k = accel_k(D(domain_list)->k, F, env, D(domain_list)->k_params);
1153 //printf("x %g\tF %g\tdt %g\tv dt %g\tk %g\n", x, F, dt, v*dt, k);
1155 assert(dtU > 1e-14); /* timestep to valid k too small */
1156 dtUCur = target_prob / k; /* safe timestep back from x+dtU */
1158 return dt; /* dtU is safe. */
1160 /* dtU wasn't safe, lets see what would be. */
1161 while (dtU > 1.1*dtL) { /* until the range is reasonably small */
1162 dt = (dtU + dtL) / 2.0;
1163 F = find_tension(num_tension_handlers, tension_handlers, domain_list, env, x+v*dt, 0);
1164 //printf("Try for dt = %g (F = %g) (dt bounds %g, %g)\n", dt, F, dtL, dtU);
1165 k = accel_k(D(domain_list)->k, F, env, D(domain_list)->k_params);
1166 dtCur = target_prob / k;
1167 //printf("x %g\tF %g\tdt %g\tv dt %g\tk %g\tdtCur = %g\n", x, F, dt, v*dt, k, dtCur);
1168 if (dtCur > dt) /* safe timestep back from x+dt covers dt */
1170 else if (dtCur < dt) { /* unsafe timestep back from x+dt, but... */
1171 dtU = dt; /* ... stepping out only dtCur would be safe */
1174 break; /* dtCur = dt */
1176 return MAX(dtUCur, dtL);
1180 To determine $dt$ for an array of potentially different folded domains,
1181 we need to find the maximum $dt$ that satisfies $k dt < P$ for all domains.
1184 double determine_dt(int num_tension_handlers, one_dim_fn_t **tension_handlers, list_t *domain_list,
1185 environment_t *env, double x,
1186 double target_prob, double dt_max, double v)
1187 { /* Returns the timestep dt in seconds.
1188 * Takes the list of folded domains, target_prob between 0 and 1,
1189 * F in N, and T in K. */
1190 double dt=dt_max, new_dt;
1191 assert(target_prob > 0.0); assert(target_prob < 1.0);
1192 assert(dt_max > 0.0);
1194 while (domain_list != NULL) {
1195 if (D(domain_list)->state == DS_FOLDED) {
1196 new_dt = search_dt(num_tension_handlers, tension_handlers, domain_list, env, x, target_prob, dt, v);
1197 dt = MIN(dt, new_dt);
1199 domain_list = domain_list->next;
1205 \subsection{Domain data}
1207 Currently domains exist in two states, folded and unfolded, and the
1208 only allowed transitions are folded $\rightarrow$ unfolded. Of
1209 course, it wouldn't be too complicated to extent this to a multi-state
1210 system, with an array containing the domains group for each possible
1211 state, and a matrix of transition-rate-calculating functions.
1212 However, at this point such generality seems unnecessary at this
1214 <<domain definitions>>=
1215 enum domain_state_t {DS_FOLDED,
1219 typedef struct domain_struct {
1220 enum domain_state_t state;
1221 one_dim_fn_t *folded_handler;
1223 one_dim_fn_t *unfolded_handler;
1225 k_func_t *k; /* function returning unfolding rate */
1226 void *folded_params; /* pointer to folded parameters */
1227 void *unfolded_params; /* pointer to unfolded parameters */
1228 void *k_params; /* pointer to k parameters */
1229 destroy_data_func_t *destroy_folded;
1230 destroy_data_func_t *destroy_unfolded;
1231 destroy_data_func_t *destroy_k;
1234 /* get the domain data for the current list node */
1235 #define D(list) ((domain_t *)(list)->d)
1236 /* get the tension params for the current list node */
1237 #define D_TP(list) (((domain_t *)(list)->d)->state == DS_FOLDED \
1238 ? ((domain_t *)(list)->d)->folded_params \
1239 : ((domain_t *)(list)->d)->unfolded_params)
1241 [[k]] is a pointer to the function determining the unfolding rate for a given tension.
1242 [[folded_params]] is a pointer to the parameters used by the function pointed to by [[k]].
1243 [[unfolded_params]] is a pointer to the parameters used by the group-appropriate handler function when determining the tension.
1244 The [[destroy_*]] pointers point to functions for freeing the memory [[*_params]].
1245 We store them with the domain data so that [[destroy_domain]] doesn't have to know which type of domain it's cleaning up after.
1247 [[create_]] and [[destroy_domain]] are simple wrappers around [[malloc]] and [[free]].
1248 <<create/destroy domain>>=
1249 domain_t *create_domain(enum domain_state_t state,
1252 destroy_data_func_t *destroy_k,
1253 one_dim_fn_t *folded_handler,
1255 void *folded_params,
1256 destroy_data_func_t *destroy_folded,
1257 one_dim_fn_t *unfolded_handler,
1259 void *unfolded_params,
1260 destroy_data_func_t *destroy_unfolded)
1262 domain_t *ret = (domain_t *)malloc(sizeof(domain_t));
1263 assert(ret != NULL);
1264 if (state == DS_FOLDED) {
1265 assert(k != NULL); /* the pointer points somewhere valid */
1266 assert(*k != NULL); /* and there is something useful there */
1268 assert(state == DS_UNFOLDED);
1270 ret->folded_handler = folded_handler;
1271 ret->folded_group = folded_group;
1272 ret->unfolded_handler = unfolded_handler;
1273 ret->unfolded_group = unfolded_group;
1275 ret->folded_params = folded_params;
1276 ret->unfolded_params = unfolded_params;
1277 ret->k_params = k_params;
1278 ret->destroy_folded = destroy_folded;
1279 ret->destroy_unfolded = destroy_unfolded;
1280 ret->destroy_k = destroy_k;
1282 fprintf(stderr, "generated domain %p. state %d\n", ret, ret->state);
1287 void destroy_domain(domain_t *domain)
1290 //printf("domain %p & %p\n", *domain, domain);
1291 if (domain->destroy_folded)
1292 (*domain->destroy_folded)(domain->folded_params);
1293 if (domain->destroy_unfolded)
1294 (*domain->destroy_unfolded)(domain->unfolded_params);
1295 if (domain->destroy_k)
1296 (*domain->destroy_k)(domain->k_params);
1302 <<destroy domain list>>=
1303 void destroy_domain_list(list_t *domain_list)
1305 domain_list = head(domain_list);
1306 while (domain_list != NULL)
1307 destroy_domain((domain_t *) pop(&domain_list));
1311 \subsection{Domain model and group handling}
1313 <<group functions>>=
1314 <<get tension handler>>
1316 <<int comparison function>>
1317 <<find possible groups>>
1320 <<get active groups>>
1323 <<get tension handler>>=
1324 one_dim_fn_t *get_tension_handler(domain_t *domain)
1326 if (domain->state == DS_FOLDED)
1327 return domain->folded_handler;
1329 if (domain->state != DS_UNFOLDED)
1330 fprintf(stderr, "oops, domain %p state %d\n", domain, domain->state);
1331 assert(domain->state == DS_UNFOLDED);
1332 return domain->unfolded_handler;
1338 int get_group(domain_t *domain)
1340 if (domain->state == DS_FOLDED)
1341 return domain->folded_group;
1343 if (domain->state != DS_UNFOLDED)
1344 fprintf(stderr, "oops, domain %p state %d\n", domain, domain->state);
1345 assert(domain->state == DS_UNFOLDED);
1346 return domain->unfolded_group;
1351 We already know all possible tension classes, since they are all
1352 hardcoded into \prog. However, there may be any number of possible
1353 groups. We define a function [[find_possible_groups]] to search for
1354 possible (not neccessarily active) groups. It can search for
1355 subgroups of a single [[handler]], or by setting [[handler]] to
1356 [[NULL]], subgroups of \emph{any} handler. It returns a list of group
1358 <<find possible groups>>=
1359 list_t *find_possible_groups(list_t *list, one_dim_fn_t *handler) {
1362 while (list != NULL) {
1363 if (handler == NULL || get_tension_handler(D(list)) == handler) {
1364 push(&ret, &D(list)->folded_group, 1);
1365 push(&ret, &D(list)->unfolded_group, 1);
1371 return ret; /* no groups with this handler, no processing needed */
1373 /* sort the ret list, and remove duplicates */
1374 sort(&ret, &int_comparison);
1375 unique(&ret, &int_comparison);
1377 fprintf(stderr, "handler %p possible groups:", handler);
1379 while (list != NULL) {
1380 fprintf(stderr, " %d", *((int *)list->d));
1383 fprintf(stderr, "\n");
1389 Search a [[list]] of domains, and determine whether a particular model
1390 class and group number combination is active.
1391 <<is group active>>=
1392 int is_group_active(list_t *list, one_dim_fn_t *handler, int group)
1395 while (list != NULL) {
1396 if (get_tension_handler(D(list)) == handler && get_group(D(list)) == group)
1404 Search a [[list]] of domains, and return all domains matching a
1405 particular model class and group number.
1407 list_t *get_group_list(list_t *list, one_dim_fn_t *handler, int group)
1411 while (list != NULL) {
1412 if (get_tension_handler(D(list)) == handler && get_group(D(list)) == group) {
1413 push(&ret, D_TP(list), 1); /* add a pointer to the appropriate tension parameters to our new list. */
1415 fprintf(stderr, "push domain %p %s tension parameters %p onto active group %p %d list %p\n", D(list), D(list)->state == DS_FOLDED ? "folded" : "unfolded", D_TP(list), handler, group, ret);
1423 Because all the node data in lists returned by [[get_group_list]] is
1424 also in the main domain list, you shouldn't destroy the node data
1425 popped off when destroying the group lists. It will all get cleaned
1426 up when the main domain list is destroyed.
1428 Put all this together to scan through a list of domains and construct
1429 an array of all the active groups.
1430 <<get active groups>>=
1431 void get_active_groups(list_t *list,
1432 int num_tension_handlers, one_dim_fn_t **tension_handlers,
1433 int *pNum_active_groups, one_dim_fn_t ***pPer_group_handlers, void ***pActive_groups)
1435 void **active_groups=NULL;
1436 one_dim_fn_t *handler, **per_group_handlers=NULL;
1437 int i, num_active_groups, *group;
1438 list_t *possible_group_numbers, *active_group_list, *active_groups_list=NULL, *per_group_handlers_list=NULL;
1440 /* get all the active groups in a list */
1441 for (i=0; i<num_tension_handlers; i++) {
1442 handler = tension_handlers[i];
1443 if (handler == NULL) continue; /* tensionless handler */
1444 possible_group_numbers = head(find_possible_groups(list, handler));
1445 while (possible_group_numbers != NULL) {
1446 group = (int *)pop(&possible_group_numbers);
1447 if (is_group_active(list, handler, *group) == 1) {
1448 active_group_list = get_group_list(list, handler, *group);
1449 push(&active_groups_list, active_group_list, 1);
1450 push(&per_group_handlers_list, handler, 1);
1452 fprintf(stderr, "active group %p for %p %d headed by tension parameters %p\n", active_group_list, handler, *group, head(active_group_list)->d);
1453 list_print(stderr, active_group_list, "active group");
1459 /* allocate the array we'll be returning */
1460 num_active_groups = list_length(active_groups_list);
1461 active_groups = (void **) malloc(sizeof(void *)*num_active_groups);
1462 assert(active_groups != NULL);
1463 per_group_handlers = (one_dim_fn_t **)malloc(sizeof(one_dim_fn_t *)*num_active_groups);
1464 assert(per_group_handlers != NULL);
1466 /* populate the array we'll be returning */
1467 active_groups_list = head(active_groups_list);
1468 for (i=0; i<num_active_groups; i++) {
1469 handler = pop(&per_group_handlers_list);
1470 per_group_handlers[i] = handler;
1472 active_groups[i] = (void *) malloc(sizeof(tension_handler_data_t));
1473 assert(active_groups[i] != NULL);
1474 active_group_list = pop(&active_groups_list);
1475 ((tension_handler_data_t *)active_groups[i])->group = active_group_list;
1476 ((tension_handler_data_t *)active_groups[i])->env = NULL;
1477 ((tension_handler_data_t *)active_groups[i])->persist = NULL;
1479 assert(active_groups_list == NULL);
1480 assert(per_group_handlers_list == NULL);
1482 *pNum_active_groups = num_active_groups;
1483 *pPer_group_handlers = per_group_handlers;
1484 *pActive_groups = active_groups;
1489 \section{String parsing}
1491 For handling command line arguments, we need parse delimited strings (e.g. [["param1,param2,param3..."]]).
1492 The model handling in getopt is set up to handle a fixed number of arguments for each model,
1493 so models (like [[kramers_integ]]) that have complicated parameters (location of spline knots)
1494 need to take care of parsing those parameters themselves.
1495 We implement this parsing in [[parse.c]], define the interface in [[parse.h]], and the the unit testing in [[check_parse.c]].
1501 <<parse definitions>>
1502 <<parse declarations>>
1506 <<parse module makefile lines>>=
1507 NW_SAWSIM_MODS += parse
1508 CHECK_BINS += check_parse
1512 <<parse definitions>>=
1513 #define SEP ',' /* argument separator character */
1516 <<parse declarations>>=
1517 extern void parse_list_string(char *string, char sep, char deeper, char shallower,
1518 int *num, char ***string_array);
1519 extern void free_parsed_list(int num, char **string_array);
1522 [[parse_list_string]] allocates memory, don't forget to free it
1523 afterward with [[free_parsed_list]]. It does not alter the original.
1525 The string may start off with a [[deeper]] character
1526 (i.e. [["{x,y}"]]), and when it does, brace stripping will set leave
1527 [[x,y]], where the pointer is one character in on the copied string.
1528 However, when we go to free the memory, we need a pointer to the
1529 beginning of the string. In order to accommodate this for a string
1530 with $N$ argument, allocate a pointer array with $N+1$ elements, let
1531 the first $N$ elements point to the separated arguments, and let the
1532 last element point to the start of the copied string regardless of
1534 <<parse delimited list string functions>>=
1535 /* TODO, split out into parse.hc */
1536 static int next_delim_index(char *string, char sep, char deeper, char shallower)
1539 while (string[i] != '\0' && !(string[i] == sep && depth == 0)) {
1540 if (string[i] == deeper) {depth++;}
1541 else if (string[i] == shallower) {depth--; assert(depth >= 0);}
1547 void parse_list_string(char *string, char sep, char deeper, char shallower,
1548 int *num, char ***string_array)
1550 char *str=NULL, **ret=NULL;
1552 if (string==NULL || strlen(string) == 0) { /* handle the trivial cases */
1554 *string_array = NULL;
1557 /* make a copy of the string, so we don't change the original */
1558 str = (char *)malloc(sizeof(char)*(strlen(string)+1));
1559 assert(str != NULL);
1560 strcpy(str, string); /* we know str is long enough */
1561 /* count the number of regions, so we can allocate pointers to them */
1564 n++; i++; /* move on to next argument */
1565 i += next_delim_index(str+i, sep, deeper, shallower);
1566 //fprintf( stderr, "delim at %d (%d) ('%s' -> '%s')\n", i, (int)str[i], str, str+i+1); fflush(stderr);
1567 } while (str[i] != '\0');
1568 ret = (char **)malloc(sizeof(char *)*(n+1));
1569 assert(ret != NULL);
1570 /* replace the separators with '\0' & assign pointers */
1571 ret[n] = str; /* point to the front of the copied string */
1574 for(i=1; i<n; i++) {
1575 j += next_delim_index(str+j, sep, deeper, shallower);
1577 ret[i] = str+j; /* point to the separated arguments */
1579 /* strip off bounding braces, if any */
1580 for(i=0; i<n; i++) {
1581 if (ret[i][0] == deeper) {
1585 if (ret[i][strlen(ret[i])-1] == shallower)
1586 ret[i][strlen(ret[i])-1] = '\0';
1589 *string_array = ret;
1592 void free_parsed_list(int num, char **string_array)
1595 /* frees all items, since they were allocated as a single string*/
1596 free(string_array[num]);
1597 /* and free the array of pointers */
1603 \subsection{Parsing implementation}
1609 <<parse delimited list string functions>>
1613 #include <assert.h> /* assert() */
1614 #include <stdlib.h> /* NULL */
1615 #include <stdio.h> /* fprintf(), stdout *//*!!*/
1616 #include <string.h> /* strlen() */
1620 \subsection{Parsing unit tests}
1622 Here we check to make sure the various functions work as expected, using \citetalias{sw:check}.
1624 <<parse check includes>>
1625 <<string comparison definition and globals>>
1626 <<check relative error>>
1627 <<parse test suite>>
1628 <<main check program>>
1631 <<parse check includes>>=
1632 #include <stdlib.h> /* EXIT_SUCCESS and EXIT_FAILURE, atof() */
1633 #include <stdio.h> /* printf() */
1634 #include <assert.h> /* assert() */
1635 #include <string.h> /* strlen() */
1640 <<parse test suite>>=
1641 <<parse list string tests>>
1642 <<parse suite definition>>
1645 <<parse suite definition>>=
1646 Suite *test_suite (void)
1648 Suite *s = suite_create ("k model");
1649 <<parse list string test case defs>>
1651 <<parse list string test case add>>
1656 <<parse list string tests>>=
1659 START_TEST(test_next_delim_index)
1661 fail_unless(next_delim_index("", ',', '{', '}')==0, NULL);
1662 fail_unless(next_delim_index(",arg,{str,ing},test", ',', '{', '}')==0, NULL);
1663 fail_unless(next_delim_index("arg,{str,ing},test", ',', '{', '}')==3, NULL);
1664 fail_unless(next_delim_index("{str,ing},test", ',', '{', '}')==9, NULL);
1665 fail_unless(next_delim_index("test", ',', '{', '}')==4, NULL);
1669 START_TEST(test_parse_list_null)
1673 parse_list_string(NULL, SEP, '{', '}',
1674 &num_param_args, ¶m_args);
1675 fail_unless(num_param_args == 0, NULL);
1676 fail_unless(param_args == NULL, NULL);
1679 START_TEST(test_parse_list_single_simple)
1683 parse_list_string("arg", SEP, '{', '}',
1684 &num_param_args, ¶m_args);
1685 fail_unless(num_param_args == 1, NULL);
1686 fail_unless(STRMATCH(param_args[0],"arg"), NULL);
1689 START_TEST(test_parse_list_single_compound)
1693 parse_list_string("{x,y,z}", SEP, '{', '}',
1694 &num_param_args, ¶m_args);
1695 fail_unless(num_param_args == 1, NULL);
1696 fail_unless(STRMATCH(param_args[0],"x,y,z"), "got '%s', expected '%s'", param_args[0], "x,y,z");
1699 START_TEST(test_parse_list_double_simple)
1703 parse_list_string("abc,def", SEP, '{', '}',
1704 &num_param_args, ¶m_args);
1705 fail_unless(num_param_args == 2, NULL);
1706 fail_unless(STRMATCH(param_args[0],"abc"), NULL);
1707 fail_unless(STRMATCH(param_args[1],"def"), NULL);
1711 <<parse list string test case defs>>=
1712 TCase *tc_parse_list_string = tcase_create("parse list string");
1714 <<parse list string test case add>>=
1715 //tcase_add_test(tc_parse_list_string, test_next_delim_index);
1716 tcase_add_test(tc_parse_list_string, test_parse_list_null);
1717 tcase_add_test(tc_parse_list_string, test_parse_list_single_simple);
1718 tcase_add_test(tc_parse_list_string, test_parse_list_single_compound);
1719 tcase_add_test(tc_parse_list_string, test_parse_list_double_simple);
1720 suite_add_tcase(s, tc_parse_list_string);
1724 \section{Unit tests}
1726 Here we check to make sure the various functions work as expected, using \citetalias{sw:check}.
1733 <<check relative error>>
1736 <<main check program>>
1748 <<determine dt tests>>
1750 <<does domain unfold tests>>
1751 <<randomly unfold domains tests>>
1752 <<suite definition>>
1755 <<suite definition>>=
1756 Suite *test_suite (void)
1758 Suite *s = suite_create ("sawsim");
1759 <<F test case defs>>
1760 <<determine dt test case defs>>
1761 <<happens test case defs>>
1762 <<does domain unfold test case defs>>
1763 <<randomly unfold domains test case defs>>
1766 <<determine dt test case add>>
1767 <<happens test case add>>
1768 <<does domain unfold test case add>>
1769 <<randomly unfold domains test case add>>
1772 tcase_add_checked_fixture(tc_strip_address,
1773 setup_strip_address,
1774 teardown_strip_address);
1780 <<main check program>>=
1784 Suite *s = test_suite();
1785 SRunner *sr = srunner_create(s);
1786 srunner_run_all(sr, CK_ENV);
1787 number_failed = srunner_ntests_failed(sr);
1789 return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
1793 \subsection{F tests}
1795 <<worm-like chain tests>>
1797 <<F test case defs>>=
1798 <<worm-like chain test case def>>
1800 <<F test case add>>=
1801 <<worm-like chain test case add>>
1804 <<worm-like chain tests>>=
1805 extern double wlc(double x, double T, double p, double L);
1806 START_TEST(test_wlc_at_zero)
1808 double T=1.0, L=1.0, p=0.1, x=0.0, lim=1e-30;
1809 fail_unless(abs(wlc(x, T, p, L)) < lim, \
1810 "abs(wlc(x=%g,T=%g,p=%g,L=%g)) = %g >= %g",
1811 x, T, p, L, abs(wlc(x, T, p, L)), lim);
1815 START_TEST(test_wlc_at_half)
1817 double T=1.0, L=1.0, p=0.1*k_B, x=0.5;
1818 /* prefactor = k_B T / p = k_B 1.0 / (k_B*0.1) = 10.0 J/nm = 10.0e21 pN
1819 * nonlinear term = 0.25 (1/(1-x/L)^2-1) = 0.25(1/.25 - 1)
1821 * linear term = x/L = 0.5
1822 * nonlinear + linear = 0.75 + 0.5 = 1.25
1823 * wlc = 10e21*1.25 = 12.5e21
1825 fail_unless(wlc(x, T, p, L)-12.5e21 < 1e16,
1826 "wlc(%g, %g, %g, %g) = %g != %g",
1827 x, T, p, L, wlc(x, T, p, L), 12.5e21);
1831 <<worm-like chain test case def>>=
1832 TCase *tc_wlc = tcase_create("WLC");
1835 <<worm-like chain test case add>>=
1836 tcase_add_test(tc_wlc, test_wlc_at_zero);
1837 tcase_add_test(tc_wlc, test_wlc_at_half);
1838 suite_add_tcase(s, tc_wlc);
1841 \subsection{Model tests}
1843 Check the searching with [[linear_k]].
1844 Check overwhelming force treatment with the heavyside-esque step [[?]].
1845 <<determine dt tests>>=
1846 double linear_k(double F, environment_t *env, void *params)
1848 double Fnot = *(double *)params;
1852 START_TEST(test_determine_dt_linear_k)
1855 double dt_max=3.0, Fnot=3.0;
1856 double F[]={0,1,2,3,4,5,6};
1857 domain_t dom; /* use both parts at once for folded/unfolded */
1861 dom->next = dom->prev = NULL;
1862 dom->k_func_t = linear_k;
1863 dom->folded_params = &Fnot;
1864 dom->unfolded_params = !!!!!!!!!
1865 dom->destroy_folded = dom->destroy_unfolded = NULL;
1866 for( i=0; i < sizeof(F)/sizeof(double); i++) {
1867 fail_unless(determine_dt(folded, unfolded, targ_p, dt_max, v, x, T)==1, NULL);
1873 <<determine dt test case defs>>=
1874 TCase *tc_determine_dt = tcase_create("Determine dt");
1876 <<determine dt test case add>>=
1877 tcase_add_test(tc_determine_dt, test_determine_dt_linear_k);
1878 suite_add_tcase(s, tc_determine_dt);
1883 <<happens test case defs>>=
1885 <<happens test case add>>=
1888 <<does domain unfold tests>>=
1890 <<does domain unfold test case defs>>=
1892 <<does domain unfold test case add>>=
1895 <<randomly unfold domains tests>>=
1897 <<randomly unfold domains test case defs>>=
1899 <<randomly unfold domains test case add>>=
1903 \section{Balancing group extensions}
1904 \label{app.tension_balance}
1906 For a given total extension $x$ (of the piezo), the various domain
1907 models (WLC, FJC, Hookean springs, \ldots) and groups extend different
1908 amounts, and we need to tweak the portion that each extends to balance
1909 the tension amongst the active groups. Since the tension balancing is
1910 separable from the bulk of the simulation, we break it out into a
1911 separate module. The interface is defined in [[tension_balance.h]],
1912 the implementation in [[tension_balance.c]], and the unit testing in
1913 [[check_tension_balance.c]]
1915 <<tension-balance.h>>=
1916 #ifndef TENSION_BALANCE_H
1917 #define TENSION_BALANCE_H
1919 <<tension balance function declaration>>
1920 #endif /* TENSION_BALANCE_H */
1923 <<tension balance functions>>=
1924 <<one dimensional bracket>>
1925 <<one dimensional solve>>
1927 <<tension balance function>>
1930 <<tension balance module makefile lines>>=
1931 NW_SAWSIM_MODS += tension_balance
1932 CHECK_BINS += check_tension_balance
1933 check_tension_balance_MODS =
1936 The entire force balancing problem reduces to a solving two nested
1937 one-dimensional root-finding problems. First we define the one
1938 dimensional tension $F(x_0)$ (where $i=0$ is the index of the first
1939 non-empty group). $x(x_0)$ is also strictly monotonically increasing,
1940 so we can solve for $x_0$ such that $\sum_i x_i = x$. [[last_x]]
1941 stores the last successful value of $x$, since we expect to be taking
1942 small steps ($x \approx$ [[last_x]]). Setting [[last_x = -1]]
1943 indicates that the groups have changed.
1944 <<tension balance function declaration>>=
1945 double tension_balance(int num_tension_groups,
1946 one_dim_fn_t **tension_handlers,
1951 <<tension balance function>>=
1952 double tension_balance(int num_tension_groups,
1953 one_dim_fn_t **tension_handlers,
1958 static x_of_xo_data_t x_xo_data = {0,NULL,NULL,NULL};
1959 double F, xo_guess, xo, lb, ub;
1960 double min_relx=1e-6, min_rely=1e-6;
1961 int max_steps=200, i;
1963 assert(num_tension_groups > 0);
1964 assert(tension_handlers != NULL);
1965 assert(params != NULL);
1966 assert(num_tension_groups > 0);
1968 if (num_tension_groups == 1) { /* only one group, no balancing required */
1971 //fprintf(stderr, "balancing for x = %g with ", x);
1972 //for(i=0; i<num_tension_groups; i++) fprintf(stderr, " %d: %g\t", i, x_xo_data.xi[i]);
1973 //fprintf(stderr, "\n");
1974 if (last_x == -1) { /* new group setup, reset x_xo_data */
1975 /* x_xo_data tension_handler and handler_data arrays freed by find_tension() */
1976 if (x_xo_data.xi != NULL)
1978 /* construct new x_xo_data */
1979 x_xo_data.n_groups = num_tension_groups;
1980 x_xo_data.tension_handler = tension_handlers;
1981 x_xo_data.handler_data = params;
1982 x_xo_data.xi = (double *) malloc(sizeof(double)*num_tension_groups);
1983 for (i=0; i<num_tension_groups; i++)
1984 x_xo_data.xi[i] = -1.0;
1986 if (last_x == -1 || 1) { /* take a rough guess and attempt boundary conditions. */
1987 if (x == 0) xo_guess = length_scale;
1988 else xo_guess = x/num_tension_groups;
1990 fprintf(stderr, "%s:\tfind a bracket for x = %g with guess of x0 = %g\n", __FUNCTION__, x, xo_guess);
1992 oneD_bracket(x_of_xo, &x_xo_data, x, xo_guess, &lb, &ub);
1993 } else { /* work off the last balanced point */
1995 fprintf(stderr, "%s:\tstep off the old bracketing x %g + %g = %g\n", __FUNCTION__, last_x, x-last_x, x);
1998 lb = x_xo_data.xi[0];
1999 ub = x_xo_data.xi[0]+ x-last_x; /* apply all change to x0 */
2000 } else if (x < last_x) {
2001 lb = x_xo_data.xi[0]- (x-last_x); /* apply all change to x0 */
2002 ub = x_xo_data.xi[0];
2003 } else { /* x == last_x */
2004 fprintf(stderr, "not moving\n");
2005 lb= x_xo_data.xi[0];
2006 ub= x_xo_data.xi[0];
2010 fprintf(stderr, "%s:\tsolve x(x0) so x = %g with bounds x0 in [%g, %g]\n",
2011 __FUNCTION__, x, lb, ub);
2013 xo = oneD_solve(x_of_xo, &x_xo_data, x, lb, ub, min_relx, min_rely, max_steps, NULL);
2014 F = x_of_xo(xo, &x_xo_data); /* HACK, bonus call to setup x_xo_data.xi */
2016 if (num_tension_groups > 1) {
2017 fprintf(stderr, "%s:\tbalanced for x = %g with ", __FUNCTION__, x);
2018 for(i=0; i<num_tension_groups; i++) fprintf(stderr, " %d: %g\t", i, x_xo_data.xi[i]);
2019 fprintf(stderr, "\n");
2023 F = (*tension_handlers[0])(xo, params[0]);
2028 <<tension balance internal definitions>>=
2029 <<x of x0 definitions>>
2032 <<x of x0 definitions>>=
2033 typedef struct x_of_xo_data_struct {
2035 one_dim_fn_t **tension_handler; /* array of fn pointers */
2036 void **handler_data; /* array of void* pointers */
2042 double x_of_xo(double xo, void *pdata)
2044 x_of_xo_data_t *data = (x_of_xo_data_t *)pdata;
2045 double F, x=0, xi, xi_guess, lb, ub;
2047 double min_relx=1e-6, min_rely=1e-6;
2049 assert(data->n_groups > 0);
2051 F = (*data->tension_handler[0])(xo, data->handler_data[0]);
2053 fprintf(stderr, "%s: finding x(x0=%g). F0(x0) = %g\n", __FUNCTION__, xo, F);
2056 if (data->xi) data->xi[0] = xo;
2057 for (i=1; i < data->n_groups; i++) {
2058 if (data->xi[i] == -1) xi_guess = length_scale; /* HACK */
2059 else xi_guess = data->xi[i];
2061 fprintf(stderr, "%s: searching for proper x[%d] for F[%d](x[%d]) = %g N\n", __FUNCTION__, i, i, i, F);
2063 oneD_bracket(data->tension_handler[i], data->handler_data[i], F, xi_guess, &lb, &ub);
2064 xi = oneD_solve(data->tension_handler[i], data->handler_data[i], F, lb, ub, min_relx, min_rely, max_steps, NULL);
2066 fprintf(stderr, "%s: found F[%d](x[%d]=%g) = %g N\n", __FUNCTION__, i, i, xi, F);
2070 if (data->xi) data->xi[i] = xi;
2073 fprintf(stderr, "%s: found x(x0=%g) = %g\n", __FUNCTION__, xo, x);
2079 Solve $f(x) = y$ to a certain precision in $x$ or $y$ in a limited
2080 number of steps for monotonically increasing $f(x)$. Simple
2081 bisection, so it's robust and converges fairly quickly. You can set
2082 the maximum number of steps to take, but if you have enough processor
2083 power, it's better to limit your precision with the relative limits
2084 [[min_relx]] and [[y]]. These ensure that the width of the bracket is
2085 small on the length scale of it's larger side. Note that \emph{both}
2086 relative limits must be satisfied for the search to stop.
2087 <<one dimensional function definition>>=
2088 /* equivalent to gsl_func_t */
2089 typedef double one_dim_fn_t(double x, void *params);
2091 <<one dimensional solve declaration>>=
2092 double oneD_solve(one_dim_fn_t *f, void *params, double y, double lb, double ub,
2093 double min_relx, double min_rely, int max_steps,
2097 <<one dimensional solve>>=
2098 /* TODO, replace with GSL one d root finder http://www.gnu.org/software/gsl/manual/html_node/One-dimensional-Root_002dFinding.html */
2099 double oneD_solve(one_dim_fn_t *f, void *params, double y, double lb, double ub,
2100 double min_relx, double min_rely, int max_steps,
2103 double yx, ylb, yub, x;
2106 ylb = (*f)(lb, params);
2107 yub = (*f)(ub, params);
2109 /* check some simple cases */
2111 if (ylb != y) return HUGE_VAL; /* error! f(x)=y not bounded */
2112 else return lb; /* any x on the range [lb, ub] would work */
2114 if (ylb == y) { x=lb; yx=ylb; goto end; }
2115 if (yub == y) { x=ub; yx=yub; goto end; }
2118 fprintf(stderr, "%s:\tstarting bracket: lb %g, x ~, ub %g\tylb %g, y %g, yub %g\n", __FUNCTION__, lb, ub, ylb, y, yub);
2120 assert(ylb < y); assert(yub > y);
2121 for (i=0; i<max_steps; i++) {
2123 yx = (*f)(x, params);
2125 fprintf(stderr, "%s:\tbracket: lb %g, x %g, ub %g\tylb %g, yx %g, yub %g\t(y %g)\n", __FUNCTION__, lb, x, ub, ylb, yx, yub, y);
2127 if (yx == y || ((ub-lb)/ub < min_relx && (yub-ylb)/yub < min_rely)) break;
2128 else if (yx > y) { ub=x; yub=yx; }
2129 else /* yx < y */ { lb=x; ylb=yx; }
2134 fprintf(stderr, "%s:\tsolved f(%g) = %g ~= %g\n", __FUNCTION__, x, yx, y);
2140 The one dimensional solver needs a bracketed solution, which sometimes we don't have.
2141 Generate bracketing $x$ values through bisection or exponential growth.
2142 <<one dimensional bracket declaration>>=
2143 void oneD_bracket(one_dim_fn_t *f, void *params, double y, double xguess, double *lb, double *ub);
2146 <<one dimensional bracket>>=
2147 void oneD_bracket(one_dim_fn_t *f, void *params, double y, double xguess, double *lb, double *ub)
2149 double yx, step, x=xguess;
2151 yx = (*f)(x, params);
2153 fprintf(stderr, "%s:\tbracketing %g, start at f(%g) = %g guessing x = %g (params %p)\n", __FUNCTION__, y, x, yx, xguess, params);
2160 fprintf(stderr, "%s:\tbracketted by [0, guess = %g]\n", __FUNCTION__, x);
2164 if (x == 0) x = length_scale/2.0; /* HACK */
2167 yx = (*f)(x, params);
2169 fprintf(stderr, "%s:\tincreasing to f(%g) = %g\n", __FUNCTION__, x, yx);
2174 fprintf(stderr, "%s:\tbracketted ouput %g by [guess = %g, %g]\n", __FUNCTION__, y, xguess, x);
2177 //fprintf(stdout, "ub %g, %g lb\n", *ub, *lb);
2181 \subsection{Balancing implementation}
2183 <<tension-balance.c>>=
2186 <<tension balance includes>>
2187 #include "tension_balance.h"
2189 double length_scale = 1e-10; /* HACK */
2191 <<tension balance internal definitions>>
2192 <<tension balance functions>>
2195 <<tension balance includes>>=
2196 #include <assert.h> /* assert() */
2197 #include <stdlib.h> /* NULL */
2198 #include <math.h> /* HUGE_VAL, macro constant, so don't need to link to math */
2199 #include <stdio.h> /* fprintf(), stdout */
2203 \subsection{Balancing unit tests}
2205 Here we check to make sure the various functions work as expected, using \citetalias{sw:check}.
2206 <<check-tension-balance.c>>=
2207 <<tension balance check includes>>
2208 <<tension balance test suite>>
2209 <<main check program>>
2212 <<tension balance check includes>>=
2213 #include <stdlib.h> /* EXIT_SUCCESS and EXIT_FAILURE */
2214 #include <assert.h> /* assert() */
2217 #include "tension_balance.h"
2220 <<tension balance test suite>>=
2221 <<tension balance function tests>>
2222 <<tension balance suite definition>>
2225 <<tension balance suite definition>>=
2226 Suite *test_suite (void)
2228 Suite *s = suite_create ("tension balance");
2229 <<tension balance function test case defs>>
2231 <<tension balance function test case adds>>
2236 <<tension balance function tests>>=
2237 <<check relative error>>
2239 double hooke(double x, void *pK)
2242 return *((double*)pK) * x;
2245 START_TEST(test_single_function)
2247 double k=5, x=3, last_x=2.0, F;
2248 one_dim_fn_t *handlers[] = {&hooke};
2249 void *data[] = {&k};
2250 F = tension_balance(1, handlers, data, last_x, x);
2251 fail_unless(F = k*x, NULL);
2256 We can also test balancing two springs with different spring constants.
2257 The analytic solution for a general number of springs is given in Appendix \ref{app.math_hooke}.
2258 <<tension balance function tests>>=
2259 START_TEST(test_double_hooke)
2261 double k1=5, k2=4, x=3, last_x=-1.0, F, Fe, x1e, x2e;
2262 one_dim_fn_t *handlers[] = {&hooke, &hooke};
2263 void *data[] = {&k1, &k2};
2264 F = tension_balance(2, handlers, data, last_x, x);
2268 //fail_unless(-(F-Fe)/Fe < 1e-6, "relative error %g > 1e-6 (Expected %g, got %g)",-(F-Fe)/Fe, Fe, F);
2269 //CHECK_ERR(1e-6, x1e, xi[0]);
2270 //CHECK_ERR(1e-6, x2e, xi[1]);
2271 CHECK_ERR(1e-6, Fe, F);
2275 TODO: test [[tension_balance()]] with another balance after the [[x_of_xo_data]] initializing balance we ran above.
2277 <<tension balance function test case defs>>=
2278 TCase *tc_tbfunc = tcase_create("tension balance function");
2281 <<tension balance function test case adds>>=
2282 tcase_add_test(tc_tbfunc, test_single_function);
2283 tcase_add_test(tc_tbfunc, test_double_hooke);
2284 suite_add_tcase(s, tc_tbfunc);
2289 The globular domains (and cantilever params, etc.) are saved in bi-directional lists.
2290 Since the list handling is so general, and separable from the bulk of the simulation, we break it out into a separate module.
2291 The interface is defined in [[list.h]], the implementation in [[list.c]], and the unit testing in [[check_list.c]]
2297 <<list definitions>>
2298 <<list declarations>>
2302 <<list declarations>>=
2303 <<head and tail declarations>>
2304 <<list length declaration>>
2305 <<push declaration>>
2307 <<list destroy declaration>>
2308 <<list to array declaration>>
2309 <<move declaration>>
2310 <<sort declaration>>
2311 <<unique declaration>>
2312 <<list print declaration>>
2316 <<create and destroy node>>
2329 <<list module makefile lines>>=
2330 NW_SAWSIM_MODS += list
2331 CHECK_BINS += check_list
2335 <<list definitions>>=
2336 typedef struct list_struct {
2337 struct list_struct *next;
2338 struct list_struct *prev;
2342 <<comparison function typedef>>
2345 [[head]] and [[tail]] return pointers to the head and tail nodes of the list:
2346 <<head and tail declarations>>=
2347 list_t *head(list_t *list);
2348 list_t *tail(list_t *list);
2351 list_t *head(list_t *list)
2355 while (list->prev != NULL) {
2361 list_t *tail(list_t *list)
2365 while (list->next != NULL) {
2372 <<list length declaration>>=
2373 int list_length(list_t *list);
2376 int list_length(list_t *list)
2383 while (list->next != NULL) {
2391 [[push]] inserts a node relative to the current position in the list
2392 without changing the current position.
2393 However, if the list we're pushing onto is [[NULL]], the current position isn't defined
2394 so we set it to that of the pushed domain.
2395 If below is zero, we insert above the current node (push(a>B>c,d,0) -> a>d>B>c),
2396 otherwise we insert below the current node (push(a>B>c,d,1) -> a>B>d>c).
2397 <<push declaration>>=
2398 void push(list_t **pList, void *data, int below);
2401 void push(list_t **pList, void *data, int below)
2403 list_t *list, *new_node;
2404 assert(pList != NULL);
2406 new_node = create_node(data);
2409 else if (below==0) { /* insert above */
2410 if (list->prev != NULL)
2411 list->prev->next = new_node;
2412 new_node->prev = list->prev;
2413 list->prev = new_node;
2414 new_node->next = list;
2415 } else { /* insert below */
2416 if (list->next != NULL)
2417 list->next->prev = new_node;
2418 new_node->next = list->next;
2419 list->next = new_node;
2420 new_node->prev = list;
2425 [[pop]] removes the current domain node, moving the current position
2426 to the node after it, unless that node is [[NULL]], in which case move
2427 the current position to the node before it.
2428 <<pop declaration>>=
2429 void *pop(list_t **pList);
2432 void *pop(list_t **pList)
2434 list_t *list, *popped;
2436 assert(pList != NULL);
2438 assert(list != NULL); /* not an empty list */
2440 /* bypass the popped node */
2441 if (list->prev != NULL)
2442 list->prev->next = list->next;
2443 if (list->next != NULL) {
2444 list->next->prev = list->prev;
2445 *pList = list->next;
2447 *pList = list->prev; /* note: list->prev may == NULL. that's ok ;) */
2449 destroy_node(popped);
2454 [[list_destroy]] just pops all the nodes off a list, possibly excuting a clean-up function on their each node's data.
2455 If the cleanup function is [[NULL]], no cleanup function is called.
2456 The nodes are not popped in any particular order.
2457 <<list destroy declaration>>=
2458 void list_destroy(list_t **pList, destroy_data_func_t *destroy);
2461 void list_destroy(list_t **pList, destroy_data_func_t *destroy)
2465 assert(pList != NULL);
2468 assert(list != NULL); /* not an empty list */
2469 while (list != NULL) {
2471 if (destroy != NULL)
2477 [[list_to_array]] converts a list to an array of pointers, preserving order.
2478 <<list to array declaration>>=
2479 void list_to_array(list_t *list, int *length, void ***array);
2482 void list_to_array(list_t *list, int *pLength, void ***pArray)
2486 assert(list != NULL);
2487 assert(pLength != NULL);
2488 assert(pArray != NULL);
2490 length = list_length(list);
2491 array = (void **)malloc(sizeof(void *)*length);
2492 assert(array != NULL);
2495 while (list != NULL) {
2496 array[i++] = list->d;
2504 [[move]] moves the current element along the list in either direction.
2505 <<move declaration>>=
2506 void move(list_t **pList, int downstream);
2509 void move(list_t **pList, int downstream)
2511 list_t *list, *flipper;
2513 assert(pList != NULL);
2514 list = *pList; /* a>B>c>d */
2515 assert(list != NULL); /* not an empty list */
2516 if (downstream == 0)
2517 flipper = list->prev; /* flipper is A */
2519 flipper = list->next; /* flipper is C */
2520 /* ensure there is somewhere to go in the direction we're heading */
2521 assert(flipper != NULL);
2522 /* remove the current element */
2523 data = pop(&list); /* data=B, a>C>d */
2524 /* and put it back in in the correct spot */
2526 if (downstream == 0) {
2527 push(&list, data, 0); /* b>A>c>d */
2528 list = list->prev; /* B>a>c>d */
2530 push(&list, data, 1); /* a>C>b>d */
2531 list = list->next; /* a>c>B>d */
2537 [[sort]] sorts a list from smallest to largest according to a user
2538 specified comparison.
2539 <<comparison function typedef>>=
2540 typedef int (comparison_fn_t)(void *A, void *B);
2543 <<int comparison function>>=
2544 int int_comparison(void *A, void *B)
2549 if (a > b) return 1;
2550 else if (a == b) return 0;
2554 <<sort declaration>>=
2555 void sort(list_t **pList, comparison_fn_t *comp);
2557 Here we use the bubble sort algorithm.
2559 void sort(list_t **pList, comparison_fn_t *comp)
2563 assert(pList != NULL);
2565 assert(list != NULL); /* not an empty list */
2566 while (stable == 0) {
2569 while (list->next != NULL) {
2570 if ((*comp)(list->d, list->next->d) > 0) {
2572 move(&list, 1 /* downstream */);
2581 [[unique]] removes duplicates from a sorted list according to a user
2582 specified comparison. Don't do this unless you have other pointers
2583 any dynamically allocated data in your list, because [[unique]] just
2584 drops any non-unique data without freeing it.
2585 <<unique declaration>>=
2586 void unique(list_t **pList, comparison_fn_t *comp);
2589 void unique(list_t **pList, comparison_fn_t *comp)
2592 assert(pList != NULL);
2594 assert(list != NULL); /* not an empty list */
2596 while (list->next != NULL) {
2597 if ((*comp)(list->d, list->next->d) == 0) {
2606 [[list_print]] prints a list to a [[FILE *]] stream.
2607 <<list print declaration>>=
2608 void list_print(FILE *file, list_t *list, char *list_name);
2611 void list_print(FILE *file, list_t *list, char *list_name)
2613 fprintf(file, "%s:", (list_name==NULL) ? "list" : list_name);
2615 while (list != NULL) {
2616 fprintf(file, " %p", list->d);
2619 fprintf(file, "\n");
2623 [[create_]] and [[destroy_node]] are simple wrappers around [[malloc]] and [[free]].
2624 <<create and destroy node>>=
2625 list_t *create_node(void *data)
2627 list_t *ret = (list_t *)malloc(sizeof(list_t));
2628 assert(ret != NULL);
2635 void destroy_node(list_t *node)
2641 The user must free the data pointed to by the node on their own.
2643 \subsection{List implementation}
2653 #include <assert.h> /* assert() */
2654 #include <stdlib.h> /* malloc(), free(), rand() */
2655 #include <stdio.h> /* fprintf(), stdout, FILE */
2656 #include "global.h" /* destroy_data_func_t */
2659 \subsection{List unit tests}
2661 Here we check to make sure the various functions work as expected, using \citetalias{sw:check}.
2663 <<list check includes>>
2665 <<main check program>>
2668 <<list check includes>>=
2669 #include <stdlib.h> /* EXIT_SUCCESS and EXIT_FAILURE */
2670 #include <stdio.h> /* FILE */
2676 <<list test suite>>=
2679 <<list suite definition>>
2682 <<list suite definition>>=
2683 Suite *test_suite (void)
2685 Suite *s = suite_create ("list");
2686 <<push test case defs>>
2687 <<pop test case defs>>
2689 <<push test case adds>>
2690 <<pop test case adds>>
2696 START_TEST(test_push)
2701 push(&list, (void *)i, 1);
2702 fail_unless(list == head(list), NULL);
2703 fail_unless( (int)list->d == 0 );
2704 for (i=0; i<n; i++) {
2705 /* we expect 0, n-1, n-2, ..., 1 */
2708 fail_unless((p=(int)pop(&list)) == e, "popped %d != %d expected", p, e);
2714 <<push test case defs>>=
2715 TCase *tc_push = tcase_create("push");
2718 <<push test case adds>>=
2719 tcase_add_test(tc_push, test_push);
2720 suite_add_tcase(s, tc_push);
2725 <<pop test case defs>>=
2727 <<pop test case adds>>=
2730 \section{Function string evaluation}
2732 For the saddle-point approximated Kramers' model (Section \ref{sec.kramers}) we need the ability to evaluate user-supplied functions ($E(x)$, $x_{ts}(F)$, \ldots).
2733 We want the ability to handle fairly general functions, but don't want to reinvent the wheel by writing our own parser/evaluator.
2734 The solution is to run a scripting language as a subprocess accessed via pipes.
2735 We use \citetalias{sw:python} here, but it would be a simple matter to replace it with another evaluator if so desired.
2737 We spawn the subprocess using the standard [[pipe]], [[fork]], [[exec]] idiom.
2738 This is one of the more complicated software ideas in \prog, so we'll go into more detail than we have been.
2739 Most of this is also POSIX-specific (as far as I know), so you'll have to do some legwork to port it to non-POSIX systems.
2740 We persevere despite the difficulties, because without command-line access to new functions, the saddle-point Kramers' approximation is of very limited utiltiy.
2742 If you feel the benefits do \emph{not} outweigh the costs, or you are on a non-POSIX system (e.g. MS Windows without Cygwin), you should probably hardcode your functions in \lang.
2743 Then you can either statically or dynamically link to those hardcoded functions.
2744 While much less flexible, this approach would be a fairly simple-to-implement fallback.
2746 Because this functionality is independent of the rest of the simulation, we'll split its definition out into its own set of files.
2747 The interface is defined in [[string_eval.h]], the implementation in [[string_eval.c]], and the unit testing in [[check_string_eval.c]].
2750 #ifndef STRING_EVAL_H
2751 #define STRING_EVAL_H
2753 <<string eval setup declaration>>
2754 <<string eval function declaration>>
2755 <<string eval teardown declaration>>
2756 #endif /* STRING_EVAL_H */
2759 <<string eval module makefile lines>>=
2760 NW_SAWSIM_MODS += string_eval
2761 CHECK_BINS += check_string_eval
2762 check_string_eval_MODS =
2765 For an introduction to POSIX process control, see\\
2766 \url{http://www.ibm.com/developerworks/aix/library/au-speakingunix8/} (very simple, but a good intro.), \\
2767 \url{http://www.ibm.com/developerworks/aix/library/au-unixprocess.html} (more detail),
2768 and of course, the relavent [[man]] pages.
2770 We start our subprocess with [[execvp]], one of the [[exec]] family of functions.
2771 [[execvp]] replaces the calling process' program with a new program.
2772 The [[p]] in [[execvp]] signifies that the new program will be found by searching the the paths listed in the [[PATH]] environment variable
2773 (this may be a security hole if someone messes about with [[PATH]] before you run \prog,
2774 but if they had the ability to change [[PATH]], the security of \prog\ is the least of your worries).
2775 The new program needs command line arguments, just like it would if you were running it from a shell.
2776 The [[v]] in [[execvp]] signifies that these command line arguments will be provided as an array of [[NULL]] terminated strings,
2777 with the final array entry being a [[NULL]] pointer.
2779 Now that we know how [[execvp]] works, we store it's arguments in some definitions,
2780 to make it easy to change the evaluating subprocess to, say, Ruby, or the users personal evaluation language.
2781 <<string eval subprocess definitions>>=
2782 #define SUBPROCESS "python"
2783 //#define SUBPROCESS_ARGS {SUBPROCESS, "-ic", "import sys;sys.ps1='';sys.ps2=''", (char *)NULL}
2784 static char *SUBPROCESS_ARGS[] = {SUBPROCESS, "-ic", "import sys;sys.ps1='';sys.ps2=''", (char *)NULL};
2785 //static char *SUBPROCESS_ARGS[] = {SUBPROCESS, "-ic", "pass", (char *)NULL};
2787 The [[i]] option lets Python know that it should run in interactive mode.
2788 In it's standard mode, python reads all of the supplied instructions, and then acts on them in a single pass.
2789 In interactive mode, python acts on every instruction as soon as it is recieved.
2790 The [[c]] option signals a command line instruction for Python to execute on startup, which in our case simply turns off the default prompting ([[ps1]] and [[ps2]]), since that is mostly for human users, and our program doesn't need it.
2791 %The [[c]] option signals a command line instruction for Python to execute on startup, which in our case simply [[pass]]es so that the silly Python header information is not printed. We leave the prompts in, because we scan for them to determine when the output has completed.
2793 Since the call to [[execvp]] replaces the calling program, we need to split out original program in half useing [[fork]].
2794 The parent half of the program can continue on to run our simulation, while the child half [[exec]]s and turns into Python.
2795 [[fork]] returns two copies of the same program, executing the original code.
2796 In the child process [[fork]] returns 0, and in the parent it returns the process ID of the child.
2797 We use this difference allows to write seperate parent/child code in the section immediately following the [[fork]].
2799 We communicate with the child (Python) process using \emph{pipes}, with one process writing data into one end of the pipe, and the other process reading the data out of the other end.
2800 The [[pipe]] function creates an unnamed pipe, and returns the file descriptors for reading and writing into the new pipe.
2801 We need two pipes, one for the subprocess's [[stdin]] and one for its [[stdout]].
2802 We store the pipe file descriptors in an array of 4 [[int]]s, and use the following definitions for access.
2803 <<string eval pipe definitions>>=
2804 #define PIPE_READ 0 /* the end you read from */
2805 #define PIPE_WRITE 1 /* the end you write to */
2807 #define STDIN 0 /* initial index of stdin pair */
2808 #define STDOUT 2 /* initial index of stdout pair */
2811 So [[pfd[STDIN+PIPE_READ]]] is the file descriptor you would read from for the [[stdin]] pipe, and similarly for the other combinations.
2813 As a finishing touch, we can promote the POSIX file descriptors ([[read]]/[[write]] interface) into the more familiar [[stdio.h]] \emph{streams} ([[fprintf]]/[[fgetc]] interface) using [[fdopen]], which creates a stream from an open file descriptor.
2815 <<string eval setup declaration>>=
2816 extern void string_eval_setup(FILE **pIn, FILE **pOut);
2818 <<string eval setup definition>>=
2819 void string_eval_setup(FILE **pIn, FILE **pOut)
2822 int pfd[4]; /* file descriptors for stdin and stdout */
2824 assert(pipe(pfd+STDIN) != -1); /* stdin pair (in, out) */
2825 assert(pipe(pfd+STDOUT) != -1); /* stdout pair (in, out) */
2827 pid = fork(); /* split process into two copies */
2829 if (pid == -1) { /* fork error */
2830 perror("fork error");
2832 } else if (pid == 0) { /* child */
2833 close(pfd[STDIN+PIPE_WRITE]); /* close stdin pipe input */
2834 close(pfd[STDOUT+PIPE_READ]); /* close stdout pipe output */
2835 dup2(pfd[STDIN+PIPE_READ],0); /* wire stdin pipe output to stdin (closes original stdin) */
2836 dup2(pfd[STDOUT+PIPE_WRITE],1); /* wire stdout pipe input to stdout (closes original stdout) */
2837 execvp(SUBPROCESS, SUBPROCESS_ARGS); /* run subprocess */
2838 perror("exec error"); /* exec shouldn't return */
2840 } else { /* parent */
2841 close(pfd[STDIN+PIPE_READ]); /* close stdin pipe output */
2842 close(pfd[STDOUT+PIPE_WRITE]); /* close stdout pipe input */
2843 *pIn = fdopen(pfd[STDIN+PIPE_WRITE], "w"); /* 'upgrade' our file descriptors to streams */
2844 if ( *pIn == NULL ) {
2845 perror("fdopen (in)");
2848 *pOut = fdopen(pfd[STDOUT+PIPE_READ], "r");
2849 if ( *pOut == NULL ) {
2850 perror("fdopen (out)");
2857 To use the evaluating subprocess, we just pipe in our command, and read out the results.
2858 For the simple cases we expect here, we restrict ourselves to a single line of returned text.
2859 <<string eval function declaration>>=
2860 extern void string_eval(FILE *in, FILE *out, char *input, int buflen, char *output);
2862 <<string eval function definition>>=
2863 void string_eval(FILE *in, FILE *out, char *input, int buflen, char *output)
2866 rc = fprintf(in, "%s", input);
2867 assert(rc == strlen(input));
2870 alarm(1); /* set a one second timeout on the read */
2871 assert( fgets(output, buflen, out) != NULL );
2872 alarm(0); /* cancel the timeout */
2873 //fprintf(stderr, "eval: %s ----> %s", input, output);
2876 The [[alarm]] calls set and clear a timeout on the returned output.
2877 If the timeout expires, the process would get a [[SIGALRM]], but it doesn't have a [[SIGALRM]] handler, so it gets a [[SIGKILL]] and dies.
2878 This protects against invalid input for which a line of output is not printed to [[stdout]].
2879 Other invalid inputs (e.g. those generating multiple lines on [[stdout]] or a single line on [[stdout]] and more in [[stderr]]) are silently ignored.
2880 If you are getting strange results, check your python code seperately. TODO, better error handling.
2882 Cleanup is fairly straightforward, we just close the connecting streams from the parent side.
2883 With the stdin pipe close on the parent side, the reading child will recive the broken pipe signal [[SIGPIPE]], and closes.
2884 The parent waits to confirm the child closing, recieves the child's exit status, and cleans up to prevent zombies.
2885 As an added touch, we redirect Python's [[stderr]] before closing the pipes, otherwise it prints a blank line when it exits.
2886 <<string eval teardown declaration>>=
2887 extern void string_eval_teardown(FILE **pIn, FILE **pOut);
2890 <<string eval teardown definition>>=
2891 void string_eval_teardown(FILE **pIn, FILE **pOut)
2896 /* redirect Python's stderr */
2897 fprintf(*pIn, "sys.stderr = open('/dev/null', 'w')\n");
2901 assert( fclose(*pIn) == 0 );
2903 assert( fclose(*pOut) == 0 );
2906 /* wait for python to exit */
2908 pid = wait(&stat_loc);
2915 if (WIFEXITED(stat_loc)) {
2916 printf("child exited with status %d\n", WEXITSTATUS(stat_loc));
2917 } else if (WIFSIGNALED(stat_loc)) {
2918 printf("child terminated with signal %d\n", WTERMSIG(stat_loc));
2923 The [[while]] loop around [[wait]] protects [[wait]] from interrupting signals.
2925 \subsection{String evaluation implementation}
2929 <<string eval includes>>
2930 #include "string_eval.h"
2931 <<string eval internal definitions>>
2932 <<string eval functions>>
2935 <<string eval includes>>=
2936 #include <assert.h> /* assert() */
2937 #include <stdlib.h> /* NULL */
2938 #include <stdio.h> /* fprintf(), stdout, fdopen() */
2939 #include <string.h> /* strlen() */
2940 #include <sys/types.h> /* pid_t */
2941 #include <unistd.h> /* pipe(), fork(), execvp(), alarm() */
2942 #include <wait.h> /* wait() */
2945 <<string eval internal definitions>>=
2946 <<string eval subprocess definitions>>
2947 <<string eval pipe definitions>>
2950 <<string eval functions>>=
2951 <<string eval setup definition>>
2952 <<string eval function definition>>
2953 <<string eval teardown definition>>
2956 \subsection{String evaluation unit tests}
2958 Here we check to make sure the various functions work as expected, using \citetalias{sw:check}.
2959 <<check-string-eval.c>>=
2960 <<string eval check includes>>
2961 <<string comparison definition and globals>>
2962 <<string eval test suite>>
2963 <<main check program>>
2966 <<string eval check includes>>=
2967 #include <stdlib.h> /* EXIT_SUCCESS and EXIT_FAILURE, atof() */
2968 #include <stdio.h> /* printf() */
2969 #include <assert.h> /* assert() */
2970 #include <string.h> /* strlen() */
2971 #include <signal.h> /* SIGKILL */
2973 #include "string_eval.h"
2976 <<string eval test suite>>=
2977 <<string eval tests>>
2978 <<string eval suite definition>>
2981 <<string eval suite definition>>=
2982 Suite *test_suite (void)
2984 Suite *s = suite_create ("string eval");
2985 <<string eval test case defs>>
2987 <<string eval test case add>>
2992 <<string eval tests>>=
2993 START_TEST(test_setup_teardown)
2996 string_eval_setup(&in, &out);
2997 string_eval_teardown(&in, &out);
3000 START_TEST(test_invalid_command)
3003 char input[80], output[80]={};
3004 string_eval_setup(&in, &out);
3005 sprintf(input, "print ABCDefg\n");
3006 string_eval(in, out, input, 80, output);
3007 string_eval_teardown(&in, &out);
3010 START_TEST(test_simple_eval)
3013 char input[80], output[80]={};
3014 string_eval_setup(&in, &out);
3015 sprintf(input, "print 3+4*5\n");
3016 string_eval(in, out, input, 80, output);
3017 fail_unless(STRMATCH(output,"23\n"), NULL);
3018 string_eval_teardown(&in, &out);
3021 START_TEST(test_multiple_evals)
3024 char input[80], output[80]={};
3025 string_eval_setup(&in, &out);
3026 sprintf(input, "print 3+4*5\n");
3027 string_eval(in, out, input, 80, output);
3028 fail_unless(STRMATCH(output,"23\n"), NULL);
3029 sprintf(input, "print (3**2 + 4**2)**0.5\n");
3030 string_eval(in, out, input, 80, output);
3031 fail_unless(STRMATCH(output,"5.0\n"), NULL);
3032 string_eval_teardown(&in, &out);
3035 START_TEST(test_eval_with_state)
3038 char input[80], output[80]={};
3039 string_eval_setup(&in, &out);
3040 sprintf(input, "print 3+4*5\n");
3041 fprintf(in, "A = 3\n");
3042 sprintf(input, "print A*3\n");
3043 string_eval(in, out, input, 80, output);
3044 fail_unless(STRMATCH(output,"9\n"), NULL);
3045 string_eval_teardown(&in, &out);
3049 <<string eval test case defs>>=
3050 TCase *tc_string_eval = tcase_create("string_eval");
3052 <<string eval test case add>>=
3053 tcase_add_test(tc_string_eval, test_setup_teardown);
3054 tcase_add_test_raise_signal(tc_string_eval, test_invalid_command, SIGKILL);
3055 tcase_add_test(tc_string_eval, test_simple_eval);
3056 tcase_add_test(tc_string_eval, test_multiple_evals);
3057 tcase_add_test(tc_string_eval, test_eval_with_state);
3058 suite_add_tcase(s, tc_string_eval);
3062 \section{Accelerating function evaluation}
3064 My first version-0.3 code was running very slowly.
3065 With the options suggested in the help
3066 ([[-v1e-6 -Mhooke -A.05 -U1 -kbell -K3.3e-4,.25e-9 -mnull -Mwlc -A0.39e-9,28e-9 -F8]]),
3067 the program spent almost all of it's time in the functions [[oneD_solve]], [[wlc]], and [[wlc_handler]],
3068 making 0.66 million calls to [[oneD_solve]] and [[9.6]] million calls each to the WLC functions.
3069 That's only 15 calls per solution, so the search algorithm seems reasonable.
3070 The number of evaluation calls could be drastically reduced, however, by implementing an $x(x_0)$ lookup table.
3075 double accel_k(k_func_t *k, double F, environment_t *env, void *params);
3077 #endif /* ACCEL_K_H */
3080 <<accel k module makefile lines>>=
3081 NW_SAWSIM_MODS += accel_k
3082 #CHECK_BINS += check_accel_k
3083 check_accel_k_MODS =
3087 #include <assert.h> /* assert() */
3088 #include <stdlib.h> /* realloc(), free(), NULL */
3089 #include "global.h" /* environment_t */
3090 #include "k_model.h" /* k_func_t */
3091 #include "interp.h" /* interp_* */
3092 #include "accel_k.h"
3094 typedef struct accel_k_struct {
3095 interp_table_t *itable;
3101 /* keep an array of all the ks we accelerate, so the caller doesn't have to worry */
3102 static int num_accels = 0;
3103 static accel_k_t *accels=NULL;
3105 /* Wrap k in the standard f(x) acceleration form */
3106 static double k_wrap(double F, void *params)
3108 accel_k_t *p = (accel_k_t *)params;
3109 return (*p->k)(F, p->env, p->params);
3112 static int k_tol(double FA, double kA, double FB, double kB)
3115 if (FB-FA > 1e-12) {
3116 //printf("unacceptable tol (x %g > 1 (y %g)\n", fabs(xA-xB), fabs(yA-yB));
3117 return 1; /* unnacceptable */
3119 //printf("acceptable tol\n");
3120 return 0; /* acceptable */
3124 static int add_accel_k(k_func_t *k, environment_t *env, void *params)
3127 accels = (accel_k_t *)realloc(accels, sizeof(accel_k_t) * ++num_accels);
3128 assert(accels != NULL);
3129 accels[i].itable = interp_table_allocate(&k_wrap, &k_tol);
3131 accels[i].env = env;
3132 accels[i].params = params;
3139 for (i=0; i<num_accels; i++)
3140 interp_table_free(accels[i].itable);
3142 if (accels) free(accels);
3146 double accel_k(k_func_t *k, double F, environment_t *env, void *params)
3149 for (i=0; i<num_accels; i++) {
3150 if (accels[i].k == k && accels[i].env == env && accels[i].params == params) {
3151 /* We've already setup this function.
3152 * WARNING: we're assuming param and env are the same. */
3153 return interp_table_eval(accels[i].itable, accels+i, F);
3156 /* set up a new acceleration environment */
3157 i = add_accel_k(k, env, params);
3158 return interp_table_eval(accels[i].itable, accels+i, F);
3162 \section{Tension models}
3163 \label{sec.tension_models}
3165 TODO: tension model intro.
3166 See \url{http://web.mit.edu/course/3/3.11/www/pset03/Rec9.pdf} for a quick introduction to worm-like and freely-jointed chain models.
3168 Because these models are independent of the rest of the simulation, we'll split their definitions out into their own set of files.
3169 The interface is defined in [[tension_model.h]], the implementation in [[tension_model.c]], the unit testing in [[check_k_model.c]], and some other tidbits in [[tension_model_utils.c]].
3171 <<tension-model.h>>=
3172 #ifndef TENSION_MODEL_H
3173 #define TENSION_MODEL_H
3175 <<tension handler types>>
3176 <<constant tension model declarations>>
3177 <<hooke tension model declarations>>
3178 <<worm-like chain tension model declarations>>
3179 <<freely-jointed chain tension model declarations>>
3180 <<find tension definitions>>
3181 <<tension model global declarations>>
3182 #endif /* TENSION_MODEL_H */
3185 <<tension model module makefile lines>>=
3186 NW_SAWSIM_MODS += tension_model
3187 #CHECK_BINS += check_tension_model
3188 check_tension_model_MODS =
3190 <<tension model utils makefile lines>>=
3191 TENSION_MODEL_MODS = tension_model parse list tension_balance
3192 TENSION_MODEL_SRC = $(BUILD_DIR)/tension_model_utils.c $(BUILD_DIR)/global.h \
3193 $(TENSION_MODEL_MODS:%=$(BUILD_DIR)/%.c) \
3194 $(TENSION_MODEL_MODS:%=$(BUILD_DIR)/%.h)
3195 $(BUILD_DIR)/tension_model_utils.c : $(SRC_DIR)/sawsim.nw | $(BUILD_DIR)
3196 notangle -Rtension-model-utils.c $< > $@
3197 $(BIN_DIR)/tension_model_utils : $(TENSION_MODEL_SRC) | $(BIN_DIR)
3198 gcc -g -o $@ $< $(TENSION_MODEL_MODS:%=$(BUILD_DIR)/%.c) $(LIBS:%=-l%)
3199 clean_tension_model_utils :
3200 rm -f $(BUILD_DIR)/tension_model_utils.c $(BIN_DIR)/tension_model_utils
3201 @ The pipe symbol [[|]] marks the prerequisits following it (in this
3202 case, the directories) as ‘order-only’ prerequisites. The timestamp
3203 on these prerequisits does not effect whether the rules are executed.
3204 This is appropriate for directories, where we don't need to recompile
3205 after an unrelated has been added to the directory, but only when the
3206 source prerequisites change. See the [[make]] documentation for more
3208 (\url{http://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html}).
3211 \label{sec.null_tension}
3213 For unstretchable domains.
3215 <<null tension model getopt>>=
3216 {NULL, "null", "an unstretchable domain", 0, NULL, NULL, NULL, NULL}
3219 \subsection{Constant}
3220 \label{sec.const_tension}
3222 <<constant tension functions>>=
3223 <<constant tension function>>
3224 <<constant tension parameter create/destroy functions>>
3227 <<constant tension model declarations>>=
3228 <<constant tension function declaration>>
3229 <<constant tension parameter create/destroy function declarations>>
3230 <<constant tension model global declarations>>
3234 An infinitely stretchable domain providing a constant tension.
3236 <<constant tension function declaration>>=
3237 extern double const_tension_handler(double x, void *pdata);
3239 <<constant tension function>>=
3240 double const_tension_handler(double x, void *pdata)
3242 tension_handler_data_t *data = (tension_handler_data_t *)pdata;
3243 list_t *list = data->group;
3248 assert(list != NULL); /* empty active group?! */
3249 F = ((const_tension_param_t *)list->d)->F;
3251 fprintf(stderr, "%s: tension %g N\n", __FUNCTION__, F);
3253 while (list != NULL) {
3254 assert(((const_tension_param_t *)list->d)->F == F);
3262 <<constant tension structure>>=
3263 typedef struct const_tension_param_struct {
3264 double F; /* tension (force) in N */
3265 } const_tension_param_t;
3269 <<constant tension parameter create/destroy function declarations>>=
3270 extern void *string_create_const_tension_param_t(char **param_strings);
3271 extern void destroy_const_tension_param_t(void *p);
3274 <<constant tension parameter create/destroy functions>>=
3275 const_tension_param_t *create_const_tension_param_t(double F)
3277 const_tension_param_t *ret
3278 = (const_tension_param_t *) malloc(sizeof(const_tension_param_t));
3279 assert(ret != NULL);
3284 void *string_create_const_tension_param_t(char **param_strings)
3286 return create_const_tension_param_t(atof(param_strings[0]));
3289 void destroy_const_tension_param_t(void *p)
3297 <<constant tension model global declarations>>=
3298 extern int num_const_tension_params;
3299 extern char *const_tension_param_descriptions[];
3300 extern char const_tension_param_string[];
3303 <<constant tension model globals>>=
3304 int num_const_tension_params = 1;
3305 char *const_tension_param_descriptions[] = {"tension F, N"};
3306 char const_tension_param_string[] = "0";
3309 <<constant tension model getopt>>=
3310 {&const_tension_handler, "const", "an infinitely stretchable domain with constant tension", 1, const_tension_param_descriptions, const_tension_param_string, &string_create_const_tension_param_t, &destroy_const_tension_param_t}
3316 <<hooke functions>>=
3318 <<hooke parameter create/destroy functions>>
3321 <<hooke tension model declarations>>=
3322 <<hooke tension function declaration>>
3323 <<hooke tension parameter create/destroy function declarations>>
3324 <<hooke tension model global declarations>>
3328 The tension of a single spring is given by $F=kx$ for some spring constant $k$.
3329 The behavior of a series of springs $k_i$ in series is given by
3331 F = \p({\sum_i \frac{1}{k_i}})^{-1} x
3333 For a simple proof, see Appendix \ref{app.math_hooke}.
3335 <<hooke tension function declaration>>=
3336 extern double hooke_handler(double x, void *pdata);
3340 double hooke_handler(double x, void *pdata)
3342 tension_handler_data_t *data = (tension_handler_data_t *)pdata;
3343 list_t *list = data->group;
3348 assert(list != NULL); /* empty active group?! */
3349 while (list != NULL) {
3350 assert( ((hooke_param_t *)list->d)->k > 0 );
3351 k += 1.0/ ((hooke_param_t *)list->d)->k;
3353 fprintf(stderr, "%s: Hooke spring %g N/m in series\n",
3354 __FUNCTION__, ((hooke_param_t *)list->d)->k);
3360 fprintf(stderr, "%s: effective Hooke spring %g N/m at %g m -> F = %g N\n",
3361 __FUNCTION__, k, x, k*x);
3367 <<hooke structure>>=
3368 typedef struct hooke_param_struct {
3369 double k; /* spring constant in N/m */
3373 <<hooke tension parameter create/destroy function declarations>>=
3374 extern void *string_create_hooke_param_t(char **param_strings);
3375 extern void destroy_hooke_param_t(void *p);
3378 <<hooke parameter create/destroy functions>>=
3379 hooke_param_t *create_hooke_param_t(double k)
3381 hooke_param_t *ret = (hooke_param_t *) malloc(sizeof(hooke_param_t));
3382 assert(ret != NULL);
3387 void *string_create_hooke_param_t(char **param_strings)
3389 return create_hooke_param_t(atof(param_strings[0]));
3392 void destroy_hooke_param_t(void *p)
3399 <<hooke tension model global declarations>>=
3400 extern int num_hooke_params;
3401 extern char *hooke_param_descriptions[];
3402 extern char hooke_param_string[];
3405 <<hooke tension model globals>>=
3406 int num_hooke_params = 1;
3407 char *hooke_param_descriptions[] = {"spring constant k, N/m"};
3408 char hooke_param_string[]="0.05";
3411 <<hooke tension model getopt>>=
3412 {hooke_handler, "hooke", "a Hookean spring (F=kx)", num_hooke_params, hooke_param_descriptions, (char *)hooke_param_string, &string_create_hooke_param_t, &destroy_hooke_param_t}
3415 \subsection{Worm-like chain}
3418 We can model several unfolded domains with a single worm-like chain.
3419 <<worm-like chain functions>>=
3420 <<worm-like chain function>>
3421 <<worm-like chain handler>>
3422 <<worm-like chain create/destroy functions>>
3425 <<worm-like chain tension model declarations>>=
3426 <<worm-like chain handler declaration>>
3427 <<worm-like chain create/destroy function declarations>>
3428 <<worm-like chain tension model global declarations>>
3432 The combination of all unfolded domains is modeled as a worm like chain,
3433 with the force $F_{\text{WLC}}$ approximately given by
3435 F_{\text{WLC}} \approx \frac{k_B T}{p}
3436 \p[{\frac{1}{4} \p({\frac{1}{(1-x/L)^2} - 1}) +\frac{x}{L}}],
3438 where $x$ is the distance between the fixed ends,
3439 $k_B$ is Boltzmann's constant,
3440 $T$ is the absolute temperature,
3441 $p$ is the persistence length, and
3442 $L$ is the total contour length of all unfolded domains\citep{bustamante94,marko95,hatfield99}.
3443 <<worm-like chain function>>=
3444 double wlc(double x, double T, double p, double L)
3446 double xL=0.0; /* uses global k_B */
3448 assert(T > 0); assert(p > 0); assert(L > 0);
3449 if (x >= L) return HUGE_VAL;
3450 xL = x/L; /* unitless */
3451 //printf("T %g\tp %g\tL %g\tx/L %g\tk %g\n", T, p, L, x/L,
3452 // k_B*T/p * ( 0.25*(1.0/((1-xL)*(1-xL))-1) + xL ));
3453 return k_B*T/p * ( 0.25*(1.0/((1-xL)*(1-xL))-1) + xL );
3456 This model assumes that each unfolded domain has the same persistence length.
3458 <<worm-like chain handler declaration>>=
3459 extern double wlc_handler(double x, void *pdata);
3462 <<worm-like chain handler>>=
3463 double wlc_handler(double x, void *pdata)
3465 tension_handler_data_t *data = (tension_handler_data_t *)pdata;
3466 list_t *list = data->group;
3470 assert(list != NULL); /* empty active group?! */
3471 p = ((wlc_param_t *)list->d)->p;
3472 while (list != NULL) {
3473 /* enforce consistent p */
3474 assert( ((wlc_param_t *)list->d)->p == p);
3475 L += ((wlc_param_t *)list->d)->L;
3477 fprintf(stderr, "%s: WLC section %g m long in series\n",
3478 __FUNCTION__, ((wlc_param_t *)list->d)->L);
3483 fprintf(stderr, "%s: WLC(x=%g m, T=%g K, p=%g m, L=%g m) = %g N\n",
3484 __FUNCTION__, x, data->env->T, p, L, wlc(x, data->env->T, p, L));
3486 return wlc(x, data->env->T, p, L);
3490 <<worm-like chain structure>>=
3491 typedef struct wlc_param_struct {
3492 double p; /* WLC persistence length (m) */
3493 double L; /* WLC contour length (m) */
3497 <<worm-like chain create/destroy function declarations>>=
3498 extern void *string_create_wlc_param_t(char **param_strings);
3499 extern void destroy_wlc_param_t(void *p /* wlc_param_t * */);
3502 <<worm-like chain create/destroy functions>>=
3503 wlc_param_t *create_wlc_param_t(double p, double L)
3505 wlc_param_t *ret = (wlc_param_t *) malloc(sizeof(wlc_param_t));
3506 assert(ret != NULL);
3512 void *string_create_wlc_param_t(char **param_strings)
3514 return create_wlc_param_t(atof(param_strings[0]), atof(param_strings[1]));
3517 void destroy_wlc_param_t(void *p /* wlc_param_t * */)
3524 We define [[wlc_param_string]] as a global array because hard-coding the values into the tension model getopt structure gives a read-only version.
3525 TODO (now we copy the string before parsing, but still do this for clarity).
3527 <<valid string write code>>=
3528 char string[] = "abc";
3531 <<valid string write code>>=
3532 char *string = "abc";
3536 <<worm-like chain tension model global declarations>>=
3537 extern int num_wlc_params;
3538 extern char *wlc_param_descriptions[];
3539 extern char wlc_param_string[];
3541 <<worm-like chain tension model globals>>=
3542 int num_wlc_params = 2;
3543 char *wlc_param_descriptions[] = {"persistence length p, m", "contour length L, m"};
3544 char wlc_param_string[]="0.39e-9,28.4e-9";
3547 <<worm-like chain tension model getopt>>=
3548 {wlc_handler, "wlc", "a worm-like chain (F=kbT/p{.25[(1-x/L)^-2 -1] + x/L})", num_wlc_params, wlc_param_descriptions, (char *)wlc_param_string, &string_create_wlc_param_t, &destroy_wlc_param_t}
3550 Titin parameters from Carrion-Vazquez 1999, PNAS USA 96, 3696.
3552 \subsection{Freely-jointed chain}
3555 <<freely-jointed chain functions>>=
3556 <<freely-jointed chain function>>
3557 <<freely-jointed chain handler>>
3558 <<freely-jointed chain create/destroy functions>>
3561 <<freely-jointed chain tension model declarations>>=
3562 <<freely-jointed chain handler declaration>>
3563 <<freely-jointed chain create/destroy function declarations>>
3564 <<freely-jointed chain tension model global declarations>>
3568 The freely jointed chain model approximates the protein as a series of rigid links of length $l$ with freely rotating joints.
3569 The tension of a chain of $N$ such links is given by
3571 F(x) = \frac{k_B T}{l} \mathcal{L}^{-1}\p({\frac{x}{L}}) \;,
3573 where $L=Nl$ is the total length of the chain, and $\mathcal{L}(\alpha) \equiv \coth{\alpha} - \frac{1}{\alpha}$ is the Langevin function\citep{hatfield99}.
3574 <<freely-jointed chain function>>=
3575 double langevin(double x, void *params)
3578 return 1.0/tanh(x) - 1/x;
3580 <<one dimensional bracket declaration>>
3581 <<one dimensional solve declaration>>
3582 double inv_langevin(double x)
3584 double lb=0.0, ub=1.0, ret;
3585 oneD_bracket(&langevin, NULL, x, 1.0, &lb, &ub);
3586 ret = oneD_solve(&langevin, NULL, x, lb, ub, 1e-6, 1e-6, 300, NULL); /* TODO, break out oneD_* to own file */
3590 double fjc(double x, double T, double l, int N)
3592 double L = l*(double)N;
3593 if (x == 0) return 0;
3594 //printf("x %g\tL%g\tx/L %g\n", x, L, x/L);
3595 return k_B*T/l * inv_langevin(x/L);
3599 <<freely-jointed chain handler declaration>>=
3600 extern double fjc_handler(double x, void *pdata);
3602 <<freely-jointed chain handler>>=
3603 double fjc_handler(double x, void *pdata)
3605 tension_handler_data_t *data = (tension_handler_data_t *)pdata;
3606 list_t *list = data->group;
3611 assert(list != NULL); /* empty active group?! */
3612 l = ((fjc_param_t *)list->d)->l;
3613 while (list != NULL) {
3614 /* enforce consistent l */
3615 assert( ((fjc_param_t *)list->d)->l == l);
3616 N += ((fjc_param_t *)list->d)->N;
3618 fprintf(stderr, "%s: FJC section %d links long in series\n",
3619 __FUNCTION__, ((fjc_param_t *)list->d)->N);
3624 fprintf(stderr, "%s: FJC(x=%g m, T=%g K, l=%g m, N=%d m) = %g N\n",
3625 __FUNCTION__, x, data->env->T, l, N, fjc(x, data->env->T, l, N));
3627 return fjc(x, data->env->T, l, N);
3631 <<freely-jointed chain structure>>=
3632 typedef struct fjc_param_struct {
3633 double l; /* FJC link length (m) */
3634 int N; /* FJC number of links */
3638 <<freely-jointed chain create/destroy function declarations>>=
3639 extern void *string_create_fjc_param_t(char **param_strings);
3640 extern void destroy_fjc_param_t(void *p);
3643 <<freely-jointed chain create/destroy functions>>=
3644 fjc_param_t *create_fjc_param_t(double l, double N)
3646 fjc_param_t *ret = (fjc_param_t *) malloc(sizeof(fjc_param_t));
3647 assert(ret != NULL);
3653 void *string_create_fjc_param_t(char **param_strings)
3655 return create_fjc_param_t(atof(param_strings[0]), atof(param_strings[1]));
3658 void destroy_fjc_param_t(void *p)
3665 <<freely-jointed chain tension model global declarations>>=
3666 extern int num_fjc_params;
3667 extern char *fjc_param_descriptions[];
3668 extern char fjc_param_string[];
3671 <<freely-jointed chain tension model globals>>=
3672 int num_fjc_params = 2;
3673 char *fjc_param_descriptions[] = {"link length l, m", "link count N, unitless integer"};
3674 char fjc_param_string[]="0.5e-9,200";
3677 <<freely-jointed chain tension model getopt>>=
3678 {fjc_handler, "fjc", "a freely-jointed chain [F=kbT/l L^-1(x/L)]", num_fjc_params, fjc_param_descriptions, (char *)fjc_param_string, &string_create_fjc_param_t, &destroy_fjc_param_t}
3681 \subsection{Tension model implementation}
3683 <<tension-model.c>>=
3686 <<tension model includes>>
3687 #include "tension_model.h"
3688 <<tension model internal definitions>>
3689 <<tension model globals>>
3690 <<tension model functions>>
3693 <<tension model includes>>=
3694 #include <assert.h> /* assert() */
3695 #include <stdlib.h> /* NULL */
3696 #include <math.h> /* HUGE_VAL, sqrt(), exp() */
3697 #include <stdio.h> /* fprintf(), stdout */
3700 #include "tension_balance.h" /* oneD_*() */
3703 <<tension model internal definitions>>=
3704 <<constant tension structure>>
3706 <<worm-like chain structure>>
3707 <<freely-jointed chain structure>>
3710 <<tension model globals>>=
3711 <<tension handler array global>>
3712 <<constant tension model globals>>
3713 <<hooke tension model globals>>
3714 <<worm-like chain tension model globals>>
3715 <<freely-jointed chain tension model globals>>
3718 <<tension model functions>>=
3719 <<constant tension functions>>
3721 <<worm-like chain functions>>
3722 <<freely-jointed chain functions>>
3726 \subsection{Utilities}
3728 The tension models can get complicated, and users may want to reassure
3729 themselves that this portion of the input physics are functioning properly. The
3730 stand-alone program [[tension_model_utils]] demonstrates the tension model
3731 interface without the overhead of having to understand a full simulation.
3732 [[tension_model_utils]] takes command line model arguments like the full
3733 simulation, and prints $F(x)$ data to the screen for the selected model over a
3736 <<tension-model-utils.c>>=
3738 <<tension model utility includes>>
3739 <<tension model utility definitions>>
3740 <<tension model utility getopt functions>>
3742 <<tension model utility main>>
3745 <<tension model utility main>>=
3746 int main(int argc, char **argv)
3748 <<tension model getopt array definition>>
3749 tension_model_getopt_t *model = NULL;
3753 one_dim_fn_t *tension_handler[NUM_TENSION_MODELS] = {0};
3754 tension_handler_data_t tdata;
3755 int num_param_args; /* for INIT_MODEL() */
3756 char **param_args; /* for INIT_MODEL() */
3758 get_options(argc, argv, &env, NUM_TENSION_MODELS, tension_models, &model,
3759 &Fmax, &Xmax, &flags);
3761 if (flags & VFLAG) {
3762 printf("#initializing model %s with parameters %s\n", model->name, model->params);
3764 INIT_MODEL("utility", model, model->params, params);
3766 push(&tdata.group, params, 1);
3768 tdata.persist = NULL;
3769 if (model->handler == NULL) {
3770 printf("No tension function for model %s\n", model->name);
3776 printf("#Distance (m)\tForce (N)\n");
3777 for (i=0; i<=N; i++) {
3778 x = Xmax*i/(double)N;
3779 F = (*model->handler)(x, &tdata);
3780 if (F < 0 || F > Fmax) break;
3781 printf("%g\t%g\n", x, F);
3783 if (flags & VFLAG && i <= N) {
3784 /* explain exit condition */
3786 printf("Impossible force %g\n", F);
3788 printf("Reached large force limit %g > %g\n", F, Fmax);
3791 params = pop(&tdata.group);
3792 if (model->destructor)
3793 (*model->destructor)(params);
3798 <<tension model utility includes>>=
3801 #include <string.h> /* strlen() */
3802 #include <assert.h> /* assert() */
3806 #include "tension_model.h"
3809 <<tension model utility definitions>>=
3810 <<version definition>>
3811 #define VFLAG 1 /* verbose */
3812 <<string comparison definition and globals>>
3813 <<tension model getopt definitions>>
3814 <<initialize model definition>>
3818 <<tension model utility getopt functions>>=
3819 <<index tension model>>
3820 <<tension model utility help>>
3821 <<tension model utility get options>>
3824 <<tension model utility help>>=
3825 void help(char *prog_name,
3827 int n_tension_models, tension_model_getopt_t *tension_models,
3828 int tension_model, double Fmax, double Xmax)
3831 printf("usage: %s [options]\n", prog_name);
3832 printf("Version %s\n\n", VERSION);
3833 printf("A utility for understanding the available tension models\n\n");
3834 printf("Environmental options:\n");
3835 printf("-T T\tTemperature T (currently %g K)\n", env->T);
3836 printf("-C T\tYou can also set the temperature T in Celsius\n");
3837 printf("Model options:\n");
3838 printf("-m model\tFolded tension model (currently %s)\n",
3839 tension_models[tension_model].name);
3840 printf("-a args\tFolded tension model argument string (currently %s)\n",
3841 tension_models[tension_model].params);
3842 printf("F(x) is calculated for a range of x and printed\n");
3843 printf("For example:\n");
3844 printf(" #Distance (m)\tForce (N)\n");
3845 printf(" 123.456\t7.89\n");
3847 printf("-F\tSet the maximum F value for the standard mode F(x) output (currently %g)\n", Fmax);
3848 printf("-X\tSet the maximum x value for the standard mode F(x) output (currently %g)\n", Xmax);
3849 printf("-V\tChange output to verbose mode\n");
3850 printf("-h\tPrint this help and exit\n");
3852 printf("Tension models:\n");
3853 for (i=0; i<n_tension_models; i++) {
3854 printf("\t%s\t%s\n", tension_models[i].name, tension_models[i].description);
3855 for (j=0; j < tension_models[i].num_params ; j++)
3856 printf("\t\t\t%s\n", tension_models[i].param_descriptions[j]);
3857 printf("\t\tdefaults: %s\n", tension_models[i].params);
3862 <<tension model utility get options>>=
3863 void get_options(int argc, char **argv, environment_t *env,
3864 int n_tension_models, tension_model_getopt_t *tension_models,
3865 tension_model_getopt_t **model, double *Fmax, double *Xmax,
3866 unsigned int *flags)
3868 char *prog_name = NULL;
3869 char c, options[] = "T:C:m:a:F:X:Vh";
3870 int tension_model=0, num_strings;
3871 extern char *optarg;
3872 extern int optind, optopt, opterr;
3876 /* setup defaults */
3878 prog_name = argv[0];
3879 env->T = 300.0; /* K */
3883 *model = tension_models;
3885 while ((c=getopt(argc, argv, options)) != -1) {
3887 case 'T': env->T = atof(optarg); break;
3888 case 'C': env->T = atof(optarg)+273.15; break;
3890 tension_model = index_tension_model(n_tension_models, tension_models, optarg);
3891 *model = tension_models+tension_model;
3894 tension_models[tension_model].params = optarg;
3896 case 'F': *Fmax = atof(optarg); break;
3897 case 'X': *Xmax = atof(optarg); break;
3898 case 'V': *flags |= VFLAG; break;
3900 fprintf(stderr, "unrecognized option '%c'\n", optopt);
3901 /* fall through to default case */
3903 help(prog_name, env, n_tension_models, tension_models, tension_model, *Fmax, *Xmax);
3912 \section{Unfolding rate models}
3913 \label{sec.k_models}
3915 We have two main choices for determining $k$: Bell's model, which assumes the
3916 folded domains exist in a single `folded' state and make instantaneous,
3917 stochastic transitions over a free energy barrier; and Kramers' model, which
3918 treats the unfolding as a thermalized diffusion process.
3919 We deal with these options like we dealt with the different tension models: by bundling all model-specific
3920 parameters into structures, with model specific functions for determining $k$.
3922 <<k func definition>>=
3923 typedef double k_func_t(double F, environment_t *env, void *params);
3926 Because these models are independent of the rest of the simulation, we'll split their definitions out into their own set of files.
3927 The interface is defined in [[k_model.h]], the implementation in [[k_model.c]], the unit testing in [[check_k_model.c]], and some other tidbits in [[k_model_utils.c]].
3929 We go against [[noweb]]'s suggested naming scheme of $\langle \text{\emph{filename}} \rangle$,
3930 because \LaTeX\ doesn't like underscores outside math mode.
3931 Instead we replace the underscores (`\verb+_+') with hyphens (`\verb+-+').
3932 You could use spaces instead (`\verb+ +'),
3933 but then your [[notangle]]s would look like [[notangle -R'k model.h']],
3934 which I don't like as much.
3940 <<k func definition>>
3941 <<null k declarations>>
3942 <<const k declarations>>
3943 <<bell k declarations>>
3944 <<kbell k declarations>>
3945 <<kramers k declarations>>
3946 <<kramers integ k declarations>>
3947 #endif /* K_MODEL_H */
3950 <<k model module makefile lines>>=
3951 NW_SAWSIM_MODS += k_model
3952 CHECK_BINS += check_k_model
3953 check_k_model_MODS = parse string_eval
3955 [[check_k_model]] also depends on the parse module.
3957 <<k model utils makefile lines>>=
3958 K_MODEL_MODS = k_model parse string_eval
3959 K_MODEL_SRC = $(BUILD_DIR)/k_model_utils.c $(BUILD_DIR)/global.h \
3960 $(K_MODEL_MODS:%=$(BUILD_DIR)/%.c) $(K_MODEL_MODS:%=$(BUILD_DIR)/%.h)
3961 $(BUILD_DIR)/k_model_utils.c : $(SRC_DIR)/sawsim.nw | $(BUILD)
3962 notangle -Rk-model-utils.c $< > $@
3963 $(BIN_DIR)/k_model_utils : $(K_MODEL_SRC) | $(BIN_DIR)
3964 gcc -g -o $@ $< $(K_MODEL_MODS:%=$(BUILD_DIR)/%.c) $(LIBS:%=-l%)
3965 clean_k_model_utils :
3966 rm -f $(BUILD_DIR)/k_model_utils.c $(BIN_DIR)/k_model_utils
3972 For domains that are never folded.
3974 <<null k declarations>>=
3976 <<null k functions>>=
3981 <<null k model getopt>>=
3982 {"null", "a permanently unfolded domain", NULL, 0, NULL, NULL, NULL, NULL}
3985 \subsection{Constant rate model}
3988 This is a pretty boring model, but it is usefull for testing the engine.
3992 where $k_0$ is the constant unfolding rate.
3994 <<const k functions>>=
3995 <<const k function>>
3996 <<const k structure create/destroy functions>>
3999 <<const k declarations>>=
4000 <<const k function declaration>>
4001 <<const k structure create/destroy function declarations>>
4002 <<const k global declarations>>
4006 <<const k structure>>=
4007 typedef struct const_k_param_struct {
4008 double knot; /* transition rate k_0 (frac population per s) */
4012 <<const k function declaration>>=
4013 double const_k(double F, environment_t *env, void *const_k_params);
4015 <<const k function>>=
4016 double const_k(double F, environment_t *env, void *const_k_params)
4017 { /* Returns the rate constant k in frac pop per s. */
4018 const_k_param_t *p = (const_k_param_t *) const_k_params;
4020 assert(p->knot > 0);
4025 <<const k structure create/destroy function declarations>>=
4026 void *string_create_const_k_param_t(char **param_strings);
4027 void destroy_const_k_param_t(void *p);
4030 <<const k structure create/destroy functions>>=
4031 const_k_param_t *create_const_k_param_t(double knot)
4033 const_k_param_t *ret = (const_k_param_t *) malloc(sizeof(const_k_param_t));
4034 assert(ret != NULL);
4039 void *string_create_const_k_param_t(char **param_strings)
4041 return create_const_k_param_t(atof(param_strings[0]));
4044 void destroy_const_k_param_t(void *p /* const_k_param_t * */)
4051 <<const k global declarations>>=
4052 extern int num_const_k_params;
4053 extern char *const_k_param_descriptions[];
4054 extern char const_k_param_string[];
4057 <<const k globals>>=
4058 int num_const_k_params = 1;
4059 char *const_k_param_descriptions[] = {"unforced unfolding rate knot, % pop per s"};
4060 char const_k_param_string[]="1";
4063 <<const k model getopt>>=
4064 {"const", "constant rate transitions", &const_k, num_const_k_params, const_k_param_descriptions, (char *)const_k_param_string, &string_create_const_k_param_t, &destroy_const_k_param_t}
4067 \subsection{Bell's model}
4070 Quantitatively, Bell's model gives $k$ as
4072 k = k_0 \cdot e^{F dx / k_B T} \;,
4074 where $k_0$ is the unforced unfolding rate,
4075 $F$ is the applied unfolding force,
4076 $dx$ is the distance to the transition state, and
4077 $k_B T$ is the thermal energy\citep{schlierf06}.
4079 <<bell k functions>>=
4081 <<bell k structure create/destroy functions>>
4084 <<bell k declarations>>=
4085 <<bell k function declaration>>
4086 <<bell k structure create/destroy function declarations>>
4087 <<bell k global declarations>>
4091 <<bell k structure>>=
4092 typedef struct bell_param_struct {
4093 double knot; /* transition rate k_0 (frac population per s) */
4094 double dx; /* `distance to transition state' (nm) */
4098 <<bell k function declaration>>=
4099 double bell_k(double F, environment_t *env, void *bell_params);
4101 <<bell k function>>=
4102 double bell_k(double F, environment_t *env, void *bell_params)
4103 { /* Returns the rate constant k in frac pop per s.
4104 * Takes F in N, T in K, knot in frac pop per s, and dx in m.
4105 * uses global k_B in J/K */
4106 bell_param_t *p = (bell_param_t *) bell_params;
4107 assert(F >= 0); assert(env->T > 0);
4109 assert(p->knot > 0); assert(p->dx >= 0);
4111 printf("F %g\tT %g\tknot %g\tdx %g\tFdx/kbT %g\tk %g\n",
4112 F, env->T, p->knot, p->dx, F*p->dx/(k_B*env->T),
4113 p->knot * exp(F*p->dx / (k_B*env->T) ));
4114 printf("Fdx %g, kbT %g\n", F*p->dx, k_B*env->T);
4116 return p->knot * exp(F*p->dx / (k_B*env->T) );
4120 <<bell k structure create/destroy function declarations>>=
4121 void *string_create_bell_param_t(char **param_strings);
4122 void destroy_bell_param_t(void *p);
4125 <<bell k structure create/destroy functions>>=
4126 bell_param_t *create_bell_param_t(double knot, double dx)
4128 bell_param_t *ret = (bell_param_t *) malloc(sizeof(bell_param_t));
4129 assert(ret != NULL);
4135 void *string_create_bell_param_t(char **param_strings)
4137 return create_bell_param_t(atof(param_strings[0]), atof(param_strings[1]));
4140 void destroy_bell_param_t(void *p /* bell_param_t * */)
4147 <<bell k global declarations>>=
4148 extern int num_bell_params;
4149 extern char *bell_param_descriptions[];
4150 extern char bell_param_string[];
4154 int num_bell_params = 2;
4155 char *bell_param_descriptions[] = {"unforced unfolding rate knot, % pop per s", "distance to transition state, m"};
4156 char bell_param_string[]="3.3e-4,0.25e-9";
4159 <<bell k model getopt>>=
4160 {"bell", "thermalized, two-state transitions", &bell_k, num_bell_params, bell_param_descriptions, (char *)bell_param_string, &string_create_bell_param_t, &destroy_bell_param_t}
4162 Initialized with Titin I27 parameters\citep{carrion-vazquez99b}.
4163 % Titin I27 parameters from Carrion-Vazquez 1999, PNAS USA 96, 3696
4166 \subsection{Linker stiffness corrected Bell model}
4169 Walton et. al showed that the Bell model constant force approximation
4170 doesn't hold for biotin-streptavadin binding, and I extended their
4171 results to I27 for the 2009 Biophysical Society Annual
4172 Meeting\cite{walton08,king09}. More details to follow when I get done
4173 with the conference...
4175 We adjust Bell's model to
4177 k = k_0 \cdot e^{\frac{F dx - \frac{1}{2} \kappa dx^2}{k_B T}} \;,
4179 where $k_0$ is the unforced unfolding rate,
4180 $F$ is the applied unfolding force,
4181 $\kappa$ is the effective spring constant,
4182 $dx$ is the distance to the transition state, and
4183 $k_B T$ is the thermal energy\citep{schlierf06}.
4185 <<kbell k functions>>=
4186 <<kbell k function>>
4187 <<kbell k structure create/destroy functions>>
4190 <<kbell k declarations>>=
4191 <<kbell k function declaration>>
4192 <<kbell k structure create/destroy function declarations>>
4193 <<kbell k global declarations>>
4197 <<kbell k structure>>=
4198 typedef struct kbell_param_struct {
4199 double knot; /* transition rate k_0 (frac population per s) */
4200 double dx; /* `distance to transition state' (nm) */
4204 <<kbell k function declaration>>=
4205 double kbell_k(double F, environment_t *env, void *kbell_params);
4207 <<kbell k function>>=
4208 double kbell_k(double F, environment_t *env, void *kbell_params)
4209 { /* Returns the rate constant k in frac pop per s.
4210 * Takes F in N, T in K, knot in frac pop per s, and dx in m.
4211 * uses global k_B in J/K */
4212 kbell_param_t *p = (kbell_param_t *) kbell_params;
4213 assert(F >= 0); assert(env->T > 0); assert(env->stiffness >= 0);
4215 assert(p->knot > 0); assert(p->dx >= 0);
4216 return p->knot * exp((F*p->dx - 0.5*env->stiffness*(p->dx)*(p->dx))/(k_B*env->T) );
4220 <<kbell k structure create/destroy function declarations>>=
4221 void *string_create_kbell_param_t(char **param_strings);
4222 void destroy_kbell_param_t(void *p);
4225 <<kbell k structure create/destroy functions>>=
4226 kbell_param_t *create_kbell_param_t(double knot, double dx)
4228 kbell_param_t *ret = (kbell_param_t *) malloc(sizeof(kbell_param_t));
4229 assert(ret != NULL);
4235 void *string_create_kbell_param_t(char **param_strings)
4237 return create_kbell_param_t(atof(param_strings[0]), atof(param_strings[1]));
4240 void destroy_kbell_param_t(void *p /* kbell_param_t * */)
4247 <<kbell k global declarations>>=
4248 extern int num_kbell_params;
4249 extern char *kbell_param_descriptions[];
4250 extern char kbell_param_string[];
4253 <<kbell k globals>>=
4254 int num_kbell_params = 2;
4255 char *kbell_param_descriptions[] = {"unforced unfolding rate knot, % pop per s", "distance to transition state, m"};
4256 char kbell_param_string[]="3.3e-4,0.25e-9";
4259 <<kbell k model getopt>>=
4260 {"kbell", "thermalized, two-state transitions, corrected for effective linker stiffness", &kbell_k, num_kbell_params, kbell_param_descriptions, (char *)kbell_param_string, &string_create_kbell_param_t, &destroy_kbell_param_t}
4262 Initialized with Titin I27 parameters\citep{carrion-vazquez99b}.
4263 % Titin I27 parameters from Carrion-Vazquez 1999, PNAS USA 96, 3696
4266 \subsection{Kramer's model}
4269 Kramer's model gives $k$ as
4271 % \frac{1}{k} = \frac{1}{D}
4272 % \int_{x_\text{min}}^{x_\text{max}}
4273 % e^{\frac{-U_F(x)}{k_B T}}
4274 % \int_0^x e^{\frac{-U_F(x')}{k_B T}} dx'
4277 %where $D$ is the diffusion constant,
4278 %$U_F(x)$ is the free energy along the unfolding coordinate, and
4279 %$x_\text{min}$ and $x_\text{max}$ are arbitrarily chosen to be $6 k_B T$
4280 % before the minimum and after the maximum, respectively \citep{schlierf06}.
4282 k \approx \frac{D}{l_c l_{ts}} e^{-E_b/k_B T},
4284 where $D$ is the diffusion constant,
4286 l_a = \sqrt{\frac{2\pi k_B T}{(\partial^2 E(x') / \partial x'^2)_{x_a}}},
4288 are length scales where
4289 $x_c(F)$ is the location of the bound state and
4290 $x_{ts}(F)$ is the location of the transition state,
4291 $E(F,x)$ is the free energy, and
4292 $E_b(F) = E(F, x_{ts}(F)) - E(F, x_c(F))$ is the barrier energy
4293 (\citet{vanKampen07} Eqn. XIII.2.2, \citet{hanggi90} Eqn.~4.56c,
4294 \citet{evans97} Eqn.~3).
4296 <<kramers k functions>>=
4297 <<kramers k function>>
4298 <<kramers k structure create/destroy functions>>
4301 <<kramers k declarations>>=
4302 <<kramers k function declaration>>
4303 <<kramers k structure create/destroy function declarations>>
4304 <<kramers k global declarations>>
4308 <<kramers k structure>>=
4309 //typedef double kramers_x_func_t(double F, double T, double *E_params, int n);
4310 //typedef double kramers_E_func_t(double F, double T, double *E_params, int n, double x);
4312 typedef struct kramers_param_struct {
4313 double D; /* diffusion rate (um/s) */
4320 //kramers_x_func_t *xc; /* function returning metastable x (nm) */
4321 //kramers_x_func_t *xts; /* function returning transition x (nm) */
4322 //kramers_E_func_t *ddEdxx; /* function returning d^2E/dx^2 (J/nm^2) */
4323 //kramers_E_func_t *E; /* function returning E (J) */
4324 //double *E_params; /* parametrize the energy landscape */
4325 //int n_E_params; /* length of E_params array */
4329 <<kramers k function declaration>>=
4330 extern double kramers_E(double F, environment_t *env, void *kramers_params, double x); /* for k_model_utils */
4331 extern double kramers_k(double F, environment_t *env, void *kramers_params);
4333 <<kramers k function>>=
4334 double kramers_x_fn(FILE *in, FILE *out, char *fn, double F, double T)
4336 static char input[160]={0};
4337 static char output[80]={0};
4338 /* setup the environment */
4339 fprintf(in, "F = %g; T = %g\n", F, T);
4340 sprintf(input, "print repr(%s)\n", fn); /* repr() to keep full precision */
4341 string_eval(in, out, input, 80, output);
4342 return atof(output);
4345 double kramers_E_fn(FILE *in, FILE *out, char *fn, double F, double T, double x)
4347 static char input[160]={0};
4348 static char output[80]={0};
4349 /* setup the environment */
4350 fprintf(in, "F = %g;T = %g;x = %g\n", F, T, x);
4351 sprintf(input, "print repr(%s)\n", fn); /* repr() to keep full precision */
4352 string_eval(in, out, input, 80, output);
4353 return atof(output);
4356 double kramers_E(double F, environment_t *env, void *kramers_params, double x)
4358 kramers_param_t *p = (kramers_param_t *) kramers_params;
4359 return kramers_E_fn(p->in, p->out, p->E, F, env->T, x);
4362 double kramers_k(double F, environment_t *env, void *kramers_params)
4363 { /* Returns the rate constant k in frac pop per s.
4364 * Takes F in N, T in K, knot in frac pop per s, and dx in m.
4365 * uses global k_B in J/K */
4366 kramers_param_t *p = (kramers_param_t *) kramers_params;
4367 double kbT, xc, xts, lc, lts, Eb;
4368 assert(F >= 0); assert(env->T > 0);
4371 assert(p->xc != NULL); assert(p->xts != NULL);
4372 assert(p->ddEdxx != NULL); assert(p->E != NULL);
4373 assert(p->in != NULL); assert(p->out != NULL);
4375 xc = kramers_x_fn(p->in, p->out, p->xc, F, env->T);
4376 if (xc == -1.0) return -1.0; /* error (Too much force) */
4377 xts = kramers_x_fn(p->in, p->out, p->xts, F, env->T);
4378 if (xc == -1.0) return -1.0; /* error (Too much force) */
4379 lc = sqrt(2.0*M_PI*kbT /
4380 kramers_E_fn(p->in, p->out, p->ddEdxx, F, env->T, xc));
4381 lts = sqrt(-2.0*M_PI*kbT/
4382 kramers_E_fn(p->in, p->out, p->ddEdxx, F, env->T, xts));
4383 Eb = kramers_E_fn(p->in, p->out, p->E, F, env->T, xts)
4384 - kramers_E_fn(p->in, p->out, p->E, F, env->T, xc);
4385 //fprintf(stderr,"D = %g, lc = %g, lts = %g, Eb = %g, kbT = %g, k = %g\n", p->D, lc, lts, Eb, kbT, p->D/(lc*lts) * exp(-Eb/kbT));
4386 return p->D/(lc*lts) * exp(-Eb/kbT);
4390 <<kramers k structure create/destroy function declarations>>=
4391 void *string_create_kramers_param_t(char **param_strings);
4392 void destroy_kramers_param_t(void *p);
4395 <<kramers k structure create/destroy functions>>=
4396 kramers_param_t *create_kramers_param_t(double D,
4397 char *xc_fn, char *xts_fn,
4398 char *E_fn, char *ddEdxx_fn,
4399 char *global_define_string)
4400 // kramers_x_func_t *xc, kramers_x_func_t *xts,
4401 // kramers_E_func_t *ddEdxx, kramers_E_func_t *E,
4402 // double *E_params, int n_E_params)
4404 kramers_param_t *ret = (kramers_param_t *) malloc(sizeof(kramers_param_t));
4405 assert(ret != NULL);
4406 ret->xc = (char *) malloc(sizeof(char)*(strlen(xc_fn)+1));
4407 assert(ret->xc != NULL);
4408 ret->xts = (char *) malloc(sizeof(char)*(strlen(xc_fn)+1));
4409 assert(ret->xts != NULL);
4410 ret->E = (char *) malloc(sizeof(char)*(strlen(E_fn)+1));
4411 assert(ret->E != NULL);
4412 ret->ddEdxx = (char *) malloc(sizeof(char)*(strlen(ddEdxx_fn)+1));
4413 assert(ret->ddEdxx != NULL);
4415 strcpy(ret->xc, xc_fn);
4416 strcpy(ret->xts, xts_fn);
4417 strcpy(ret->E, E_fn);
4418 strcpy(ret->ddEdxx, ddEdxx_fn);
4419 //ret->E_params = E_params;
4420 //ret->n_E_params = n_E_params;
4421 string_eval_setup(&ret->in, &ret->out);
4422 fprintf(ret->in, "kB = %g\n", k_B);
4423 fprintf(ret->in, "%s\n", global_define_string);
4427 /* takes an array of 4 functions: {"(1,2),(2,0),..."} */
4428 void *string_create_kramers_param_t(char **param_strings)
4430 return create_kramers_param_t(atof(param_strings[0]),
4438 void destroy_kramers_param_t(void *p /* kramers_param_t * */)
4440 kramers_param_t *pk = (kramers_param_t *)p;
4442 string_eval_teardown(&pk->in, &pk->out);
4444 // free(pk->E_params);
4450 For our defaults, we'll follow \citet{schlierf06} and study ddFLN4.
4451 Schlierf and Rief used a cubic-spline interpolation routine and the double integral form of Kramers' theory, so we get to pick an actual function to approximate the energy landscape.
4452 We follow \citet{shillcock98} and use
4454 E(F,E_b,x,x_s) \equiv \frac{27}{4} E_b
4455 \p({\frac{x}{x_s}})^2 \p({2-3\frac{x}{x_s}})
4458 where TODO, variable meanings.%\citep{shillcock98}.
4459 The first and second derivatives of $E(F,E_b,x,x_s)$ are
4461 E'(F,E_b,x,x_s) &=\frac{27 E_b}{4 x_s}\frac{x}{x_s}\p({4-9\frac{x}{x_s}})-F\\
4462 E''(F,E_b,x,x_s)&=\frac{27 E_b}{2 x_s^2}\p({2-9\frac{x}{x_s}}).
4464 $E' = ax^2 + bx + c$, with $a=\frac{-243 E_b}{4 x_s^3}$,
4465 $b=\frac{27 E_b}{x_s^2}$, and $c=-F$.
4466 The roots are therefor at
4468 x_\pm &= -\frac{b}{2a}\pm\sqrt{\p({\frac{b}{2a}})^2 - \frac{c}{a}} \\
4469 &= \frac{2 x_s}{9}\pm\sqrt{\p({\frac{2 x_s}{9}})^2-\frac{4Fx_s^3}{243E_b}}\\
4470 &= \frac{2 x_s}{9} \p({1 \pm \sqrt{1-\frac{F x_s}{3 E_b}}})
4473 <<kramers k global declarations>>=
4474 extern int num_kramers_params;
4475 extern char *kramers_param_descriptions[];
4476 extern char kramers_param_string[];
4479 <<kramers k globals>>=
4480 int num_kramers_params = 6;
4481 char *kramers_param_descriptions[] = {"Diffusion constant D, m^2/s", "constant parameter declarations", "bound position xc, m", "transition state xts, m","energy E, J", "d^2E/dx^2, J/m^2"};
4482 char kramers_param_string[]="0.2e-12,{Eb = 20*300*kB;xs = 1.5e-9},{(F*xs > 3*Eb) and (-1) or (2*xs/9.0*(1 - (1 - F*xs/(3*Eb))**0.5))},{2*xs/9.0*(1 + (1 - F*xs/(3*Eb))**0.5)},{27.0/4 * Eb * (x/xs)**2 * (2-3*x/xs) - F*x},{27.0*Eb/(2*xs**2) * (2-9*x/xs)}";
4484 Which we initialized with parameters for ddFLN4\citep{schlierf06}.
4485 There is a bit of funny buisness in the $x_c$ definition to handle high force cases.
4486 When $F x_s > 3 E_b$, the root will be complex (negative energy barrier), and the saddle point analysis breaks down.
4487 Returning an $x_c$ position of $-1\U{m}$ lets the simulation know that the rate is poorly defined for that force, and the timestepping algorithm in Section \ref{app.adaptive_dt} reduces it's timestep appropriately.
4488 The [[(cond) and (val_1) or (val_2)]] construction is Python hackery to simulate [[C]]'s ternary operator [[(cond) ? (val_1) : (val_2)]].
4489 It works so long as [[val_1]] is not [[false]].
4491 <<kramers k model getopt>>=
4492 {"kramers", "thermalized, diffusion-limited transitions (function parameters are parsed by Python. For distances, the environment variables force 'F' and temperature 'T' are defined, as well as the Boltzmann constant 'kB'. For energies, the position 'x' is also defined. Functions must evaluate to a float representing their output and produce no output on stdout.", &kramers_k, num_kramers_params, kramers_param_descriptions, (char *)kramers_param_string, &string_create_kramers_param_t, &destroy_kramers_param_t}
4495 \subsection{Kramer's model (natural cubic splines)}
4496 \label{sec.kramers_integ}
4498 Alternatively, we could skip the gaussian approximation and evaluate the double-integral form of Kramers' overdamped model,
4499 (\citet{hanggi90} Eqn.~4.56b, \citet{vanKampen07} Eqn.~XIII.2.1,
4500 \citet{schlierf06} Eqn.~2)
4502 \frac{1}{k} = \frac{1}{D}
4503 \int_{x_\text{min}}^{x_\text{max}}
4504 e^{\frac{U_F(x)}{k_B T}}
4505 \int_0^x e^{\frac{-U_F(x')}{k_B T}} dx'
4508 where $D$ is the diffusion constant,
4509 $U_F(x)$ is the free energy along the unfolding coordinate, and
4510 $x_\text{min}$ and $x_\text{max}$ are arbitrarily chosen to be $6 k_B T$
4511 before the minimum and after the maximum, respectively (note: there is a sign difference from Schlierf)\citep{schlierf06,hanggi90}.
4513 We can parametrize the energy unforced landscape $U(x)$ by $N$ \emph{knots} (anchor points),
4514 interpolating between them with cubic splines.
4515 We'll let the \citetalias{galassi05} natural cubic spline implementation do the heavy lifting here.
4517 <<kramers integ k functions>>=
4518 <<spline functions>>
4519 <<kramers integ A k functions>>
4520 <<kramers integ B k functions>>
4521 <<kramers integ k function>>
4522 <<kramers integ k structure create/destroy functions>>
4525 <<kramers integ k declarations>>=
4526 <<kramers integ k function declaration>>
4527 <<kramers integ k structure create/destroy function declarations>>
4528 <<kramers integ k global declarations>>
4532 <<kramers integ k structure>>=
4533 typedef double func_t(double x, void *params);
4534 typedef struct kramers_integ_param_struct {
4535 double D; /* diffusion rate (um/s) */
4536 double x_min; /* integration bounds */
4538 func_t *E; /* function returning E (J) */
4539 void *E_params; /* parametrize the energy landscape */
4540 destroy_data_func_t *destroy_E_params;
4542 double F; /* for passing into GSL evaluations */
4544 } kramers_integ_param_t;
4547 <<spline param structure>>=
4548 typedef struct spline_param_struct {
4549 int n_knots; /* natural cubic spline knots for U(x) */
4552 gsl_spline *spline; /* GSL spline parameters */
4553 gsl_interp_accel *acc; /* GSL spline acceleration data */
4557 We break the evaluation of $k$ into the two integrals, and use the \citetalias{galassi05} to do the heavy lifting.
4559 \text{Integral}_A(x) \equiv \int_0^x e^{\frac{-U_F(x')}{k_B T}} dx'
4561 <<kramers integ A k functions>>=
4562 double kramers_integ_k_integrandA(double x, void *params)
4564 kramers_integ_param_t *p = (kramers_integ_param_t *)params;
4565 double U = (*p->E)(x, p->E_params);
4566 double Fx = p->F * x;
4567 double kbT = k_B * p->env->T;
4568 //fprintf(stderr, "integrandA(%g) = %g\n", x, exp((U-Fx)/kbT));
4569 return exp(-(U-Fx)/kbT);
4571 double kramers_integ_k_integralA(double x, void *params)
4573 kramers_integ_param_t *p = (kramers_integ_param_t *)params;
4575 double result, abserr;
4576 size_t neval; /* for qng */
4577 static gsl_integration_workspace *w = NULL;
4578 if (w == NULL) w = gsl_integration_workspace_alloc(500); /* for qag */
4579 f.function = &kramers_integ_k_integrandA;
4581 //assert(gsl_integration_qng(&f, p->x_min, x, 0 /* epsabs */, 1e-6, &result, &abserr, &neval) == 0);
4582 assert(gsl_integration_qag(&f, p->x_min, x, 0 /* epsabs */, 1e-6, 500, 6/* key */, w, &result, &abserr) == 0);
4583 //fprintf(stderr, "integralA = %g\n", result);
4589 \text{Integral}_B(x_\text{min}, x_\text{max}) \equiv
4590 \int_{x_\text{min}}^{x_\text{max}}
4591 e^{\frac{U_F(x)}{k_B T}}
4592 \text{Integral}_A(x)
4595 <<kramers integ B k functions>>=
4596 double kramers_integ_k_integrandB(double x, void *params)
4598 kramers_integ_param_t *p = (kramers_integ_param_t *)params;
4599 double U = (*p->E)(x, p->E_params);
4600 double Fx = p->F * x;
4601 double kbT = k_B * p->env->T;
4602 double intA = kramers_integ_k_integralA(x, params);
4603 //fprintf(stderr, "integrandB(%g) = %g\n", x, exp(-(U-Fx)/kbT))*intA;
4604 return exp((U-Fx)/kbT)*intA;
4606 double kramers_integ_k_integralB(double F, environment_t *env, void *params)
4608 kramers_integ_param_t *p = (kramers_integ_param_t *)params;
4610 double result, abserr;
4611 size_t neval; /* for qng */
4612 static gsl_integration_workspace *w = NULL;
4613 if (w == NULL) w = gsl_integration_workspace_alloc(500); /* for qag */
4614 f.function = &kramers_integ_k_integrandB;
4618 //assert(gsl_integration_qng(&f, p->x_min, p->x_max, 0 /* epsabs */, 1e-6, &result, &abserr, &neval) == 0);
4619 assert(gsl_integration_qag(&f, p->x_min, p->x_max, 0 /* epsabs */, 1e-6, 500, 6/* key */, w, &result, &abserr) == 0);
4620 //fprintf(stderr, "integralB = %g\n", result);
4625 With the integrals taken care of, evaluating $k$ is simply
4627 k = \frac{D}{\text{Integral}_B(x_\text{min}, x_\text{max})}
4629 <<kramers integ k function declaration>>=
4630 double kramers_integ_k(double F, environment_t *env, void *kramers_params);
4632 <<kramers integ k function>>=
4633 double kramers_integ_k(double F, environment_t *env, void *kramers_params)
4634 { /* Returns the rate constant k in frac pop per s. Takes F in N. */
4635 kramers_integ_param_t *p = (kramers_integ_param_t *) kramers_params;
4636 return p->D/kramers_integ_k_integralB(F, env, kramers_params);
4640 <<kramers integ k structure create/destroy function declarations>>=
4641 void *string_create_kramers_integ_param_t(char **param_strings);
4642 void destroy_kramers_integ_param_t(void *p);
4645 <<kramers integ k structure create/destroy functions>>=
4646 kramers_integ_param_t *create_kramers_integ_param_t(double D,
4647 double xmin, double xmax, func_t *E,
4649 destroy_data_func_t *destroy_E_params)
4651 kramers_integ_param_t *ret = (kramers_integ_param_t *) malloc(sizeof(kramers_integ_param_t));
4652 assert(ret != NULL);
4657 ret->E_params = E_params;
4658 ret->destroy_E_params = destroy_E_params;
4662 void *string_create_kramers_integ_param_t(char **param_strings)
4664 //printf("create kramers integ. D: %s, knots: %s, x in [%s, %s]\n",
4665 // param_strings[0],param_strings[1],param_strings[2],param_strings[3]);
4666 void *E_params = string_create_spline_param_t(param_strings+1);
4667 return create_kramers_integ_param_t(atof(param_strings[0]),
4668 atof(param_strings[2]),
4669 atof(param_strings[3]),
4670 &spline_eval, E_params,
4671 destroy_spline_param_t);
4674 void destroy_kramers_integ_param_t(void *params)
4676 kramers_integ_param_t *p = (kramers_integ_param_t *)params;
4679 (*p->destroy_E_params)(p->E_params);
4685 Finally we have the GSL spline wrappers:
4686 <<spline functions>>=
4688 <<create/destroy spline>>
4691 <<spline function>>=
4692 double spline_eval(double x, void *spline_params)
4694 spline_param_t *p = (spline_param_t *)(spline_params);
4695 return gsl_spline_eval(p->spline, x, p->acc);
4699 <<create/destroy spline>>=
4700 spline_param_t *create_spline_param_t(int n_knots, double *x, double *y)
4702 spline_param_t *ret = (spline_param_t *) malloc(sizeof(spline_param_t));
4703 assert(ret != NULL);
4704 ret->n_knots = n_knots;
4707 ret->acc = gsl_interp_accel_alloc();
4708 ret->spline = gsl_spline_alloc(gsl_interp_cspline, n_knots);
4709 gsl_spline_init(ret->spline, x, y, n_knots);
4713 /* takes an (array of strings) of length one: {"(1,2),(2,0),..."} */
4714 void *string_create_spline_param_t(char **param_strings)
4718 double *x=NULL, *y=NULL;
4719 /* break into ordered pairs */
4720 parse_list_string(param_strings[0], SEP, '(', ')',
4721 &num_knots, &knot_coords);
4722 x = (double *)malloc(sizeof(double)*num_knots);
4724 y = (double *)malloc(sizeof(double)*num_knots);
4726 for (i=0; i<num_knots; i++) {
4727 if (sscanf(knot_coords[i], "%lg,%lg", x+i, y+i) != 2) {
4728 fprintf(stderr, "Could not parse pair %d, \"%s\"\n", i, knot_coords[i]);
4731 //fprintf(stderr, "%d : scanned '%s' -> (%g, %g)\n", i, knot_coords[i], x[i], y[i]);
4733 free_parsed_list(num_knots, knot_coords);
4734 return create_spline_param_t(num_knots, x, y);
4737 void destroy_spline_param_t(void *params)
4739 spline_param_t *p = (spline_param_t *)params;
4742 gsl_spline_free(p->spline);
4744 gsl_interp_accel_free(p->acc);
4754 <<kramers integ k global declarations>>=
4755 extern int num_kramers_integ_params;
4756 extern char *kramers_integ_param_descriptions[];
4757 extern char kramers_integ_param_string[];
4760 <<kramers integ k globals>>=
4761 int num_kramers_integ_params = 4;
4762 char *kramers_integ_param_descriptions[] = {
4763 "diffusion D, m^2 / s",
4764 "spline knots, {(x1,y1),(x2,y2),...}, (m,J)",
4765 "starting integration bound x_min, m",
4766 "ending integration bound x_max, m"};
4767 //char kramers_integ_param_string[]="0.2e-12,{(3.5e-9,-5*k_B*300),(4e-9,-20*k_B*300),(4.25e-9,-8*k_B*300),(5e-9,0),(5.5e-9,-10*k_B*300)},3e-9,6e-9";
4768 //char kramers_integ_param_string[]="0.2e-12,{(3.5e-9,-2.1e-20),(4e-9,-8.3e-20),(4.25e-9,-3.3e-20),(5e-9,0),(5.5e-9,-4.1e-20)},3e-9,6e-9";
4769 char kramers_integ_param_string[]="0.2e-12,{(2e-9,0),(2.1e-9,-3.7e-20),(2.2e-9,-4.4e-20),(2.35e-9,-2.5e-20),(2.41e-9,-7e-21),(2.45e-9,8e-22),(2.51e-9,6.9e-21),(2.64e-9,1.39e-20),(2.9e-9,2.55e-20),(3.52e-9,2.9e-20),(3.7e-9,1.45e-20),(4e-9,-1.7e-20),(6e-9,-2e-20),(9e-9,-3e-20),(1e-8,-3e-20)},2e-9,6e-9";
4770 /* D from Schlierf 2006, Biophys 90, 4, L33, sup.
4771 * Anchor points from private communication with Schlierf, Apr 9th, 2008.
4772 * other parameters from from Schlierf 2006, Biophys 90, 4, L33, figure 1. */
4775 <<kramers integ k model getopt>>=
4776 {"kramers_integ", "thermalized, diffusion-limited transitions", &kramers_integ_k, num_kramers_integ_params, kramers_integ_param_descriptions, (char *)kramers_integ_param_string, &string_create_kramers_integ_param_t, &destroy_kramers_integ_param_t}
4778 Initialized with Titin I27 parameters\citep{carrion-vazquez99b}.
4779 % Titin I27 parameters from Carrion-Vazquez 1999, PNAS USA 96, 3696
4781 \subsection{Unfolding model implementation}
4785 <<k model includes>>
4786 #include "k_model.h"
4787 <<k model internal definitions>>
4789 <<k model functions>>
4792 <<k model includes>>=
4793 #include <assert.h> /* assert() */
4794 #include <stdlib.h> /* NULL, malloc() */
4795 #include <math.h> /* HUGE_VAL, sqrt(), exp() */
4796 #include <stdio.h> /* fprintf(), stdout */
4797 #include <string.h> /* strlen(), strcpy() */
4798 #include <gsl/gsl_integration.h>
4799 #include <gsl/gsl_spline.h>
4804 <<k model internal definitions>>=
4805 <<const k structure>>
4806 <<bell k structure>>
4807 <<kbell k structure>>
4808 <<kramers k structure>>
4809 <<kramers integ k structure>>
4810 <<spline param structure>>
4813 <<k model globals>>=
4818 <<kramers k globals>>
4819 <<kramers integ k globals>>
4822 <<k model functions>>=
4823 <<null k functions>>
4824 <<const k functions>>
4825 <<bell k functions>>
4826 <<kbell k functions>>
4827 <<kramers k functions>>
4828 <<kramers integ k functions>>
4831 \subsection{Unfolding model unit tests}
4833 Here we check to make sure the various functions work as expected, using \citetalias{sw:check}.
4834 <<check-k-model.c>>=
4835 <<k model check includes>>
4836 <<check relative error>>
4838 <<k model test suite>>
4839 <<main check program>>
4842 <<k model check includes>>=
4843 #include <stdlib.h> /* EXIT_SUCCESS and EXIT_FAILURE, atof() */
4844 #include <stdio.h> /* sprintf() */
4845 #include <assert.h> /* assert() */
4846 #include <math.h> /* exp() */
4849 #include "k_model.h"
4852 <<k model test suite>>=
4855 <<k model suite definition>>
4858 <<k model suite definition>>=
4859 Suite *test_suite (void)
4861 Suite *s = suite_create ("k model");
4862 <<const k test case defs>>
4863 <<bell k test case defs>>
4865 <<const k test case adds>>
4866 <<bell k test case adds>>
4871 \subsubsection{Constant}
4873 <<const k test case defs>>=
4874 TCase *tc_const_k = tcase_create("Constant k");
4877 <<const k test case adds>>=
4878 tcase_add_test(tc_const_k, test_const_k_create_destroy);
4879 tcase_add_test(tc_const_k, test_const_k_over_range);
4880 suite_add_tcase(s, tc_const_k);
4885 START_TEST(test_const_k_create_destroy)
4887 char *knot[] = {"1","2","3","4","5","6"};
4888 char *params[] = {knot[0]};
4891 for( i=0; i < sizeof(knot)/sizeof(char *); i++) {
4892 params[0] = knot[i];
4893 p = string_create_const_k_param_t(params);
4894 destroy_const_k_param_t(p);
4899 START_TEST(test_const_k_over_range)
4901 double F, Fm=0.0, FM=10, dF=.1, T=300.0;
4902 char *knot[] = {"1","2","3","4","5","6"};
4903 char *params[] = {knot[0]};
4906 char param_string[80];
4909 for( i=0; i < sizeof(knot)/sizeof(char *); i++) {
4910 params[0] = knot[i];
4911 p = string_create_const_k_param_t(params);
4912 for ( F=Fm; F<FM; F+=dF ) {
4913 fail_unless(const_k(F, &env, p)==atof(knot[i]),
4914 "const_k(%g, %g, &{%s}) = %g != %s",
4915 F, T, knot[i], const_k(F, &env, p), knot[i]);
4917 destroy_const_k_param_t(p);
4923 \subsubsection{Bell}
4925 <<bell k test case defs>>=
4926 TCase *tc_bell = tcase_create("Bell's k");
4929 <<bell k test case adds>>=
4930 tcase_add_test(tc_bell, test_bell_k_create_destroy);
4931 tcase_add_test(tc_bell, test_bell_k_at_zero);
4932 tcase_add_test(tc_bell, test_bell_k_at_one);
4933 suite_add_tcase(s, tc_bell);
4937 START_TEST(test_bell_k_create_destroy)
4939 char *knot[] = {"1","2","3","4","5","6"};
4941 char *params[] = {knot[0], dx};
4944 for( i=0; i < sizeof(knot)/sizeof(char *); i++) {
4945 params[0] = knot[i];
4946 p = string_create_bell_param_t(params);
4947 destroy_bell_param_t(p);
4952 START_TEST(test_bell_k_at_zero)
4954 double F=0.0, T=300.0;
4955 char *knot[] = {"1","2","3","4","5","6"};
4957 char *params[] = {knot[0], dx};
4960 char param_string[80];
4963 for( i=0; i < sizeof(knot)/sizeof(char *); i++) {
4964 params[0] = knot[i];
4965 p = string_create_bell_param_t(params);
4966 fail_unless(bell_k(F, &env, p)==atof(knot[i]),
4967 "bell_k(%g, %g, &{%s,%s}) = %g != %s",
4968 F, T, knot[i], dx, bell_k(F, &env, p), knot[i]);
4969 destroy_bell_param_t(p);
4974 START_TEST(test_bell_k_at_one)
4977 char *knot[] = {"1","2","3","4","5","6"};
4979 char *params[] = {knot[0], dx};
4980 double F= k_B*T/atof(dx);
4983 char param_string[80];
4986 for( i=0; i < sizeof(knot)/sizeof(char *); i++) {
4987 params[0] = knot[i];
4988 p = string_create_bell_param_t(params);
4989 CHECK_ERR(1e-6, atof(knot[i])*exp(1.0), bell_k(F, &env, p));
4990 //fail_unless(bell_k(F, &env, p)==atof(knot[i])*exp(1.0),
4991 // "bell_k(%g, %g, &{%s,%s}) = %g != %g",
4992 // F, T, knot[i], dx, bell_k(F, &env, p), atof(knot[i])*exp(1.0));
4993 destroy_bell_param_t(p);
4999 <<kramers k tests>>=
5002 <<kramers k test case def>>=
5005 <<kramers k test case add>>=
5008 <<k model function tests>>=
5009 <<check relative error>>
5011 START_TEST(test_something)
5013 double k=5, x=3, last_x=2.0, F;
5014 one_dim_fn_t *handlers[] = {&hooke};
5015 void *data[] = {&k};
5016 double xi[] = {0.0};
5018 int new_active_groups = 1;
5019 F = k_model(1, handlers, data, active, new_active_groups, xi, last_x, x);
5020 fail_unless(F = k*x, NULL);
5025 \subsection{Utilities}
5027 The unfolding models can get complicated, and are sometimes hard to explain to others.
5028 The stand-alone program [[k_model_utils]] demonstrates the unfolding model interface without
5029 the overhead of having to understand a full simulation.
5030 [[k_model_utils]] can operate in a general mode, where it prints $k(F)$ data to the screen,
5031 or special mode, where the operation depends on the specific model selected.
5033 <<k-model-utils.c>>=
5035 <<k model utility includes>>
5036 <<k model utility definitions>>
5037 <<k model utility getopt functions>>
5038 <<k model utility multi model E>>
5039 <<k model utility main>>
5042 <<k model utility main>>=
5043 int main(int argc, char **argv)
5045 <<k model getopt array definition>>
5046 k_model_getopt_t *model = NULL;
5051 int num_param_args; /* for INIT_MODEL() */
5052 char **param_args; /* for INIT_MODEL() */
5054 double special_xmin;
5055 double special_xmax;
5056 get_options(argc, argv, &env, NUM_K_MODELS, k_models, &mode, &model,
5057 &Fmax, &special_xmin, &special_xmax, &flags);
5058 if (flags & VFLAG) {
5059 printf("#initializing model %s with parameters %s\n", model->name, model->params);
5061 INIT_MODEL("utility", model, model->params, params);
5064 if (model->k == NULL) {
5065 printf("No k function for model %s\n", model->name);
5069 printf("Fmax = %g <= 0\n", Fmax);
5075 printf("#F (N)\tk (%% pop. per s)\n");
5076 for (i=0; i<=N; i++) {
5077 F = Fmax*i/(double)N;
5078 k = (*model->k)(F, &env, params);
5080 printf("%g\t%g\n", F, k);
5085 if (model->k == NULL || model->k == &bell_k) {
5086 printf("No special mode for model %s\n", model->name);
5089 if (special_xmax <= special_xmin) {
5090 printf("Xmax = %g <= %g = Xmin\n", special_xmax, special_xmin);
5094 double Xrng=(special_xmax-special_xmin), x, E;
5096 printf("#x (m)\tE (J)\n");
5097 for (i=0; i<=N; i++) {
5098 x = special_xmin + Xrng*i/(double)N;
5099 E = multi_model_E(model->k, params, &env, x);
5100 printf("%g\t%g\n", x, E);
5107 if (model->destructor)
5108 (*model->destructor)(params);
5113 <<k model utility multi model E>>=
5114 double multi_model_E(k_func_t *k, void *model_params, environment_t *env, double x)
5116 kramers_integ_param_t *p = (kramers_integ_param_t *)model_params;
5118 if (k == kramers_integ_k)
5119 E = (*p->E)(x, p->E_params);
5121 E = kramers_E(0, env, model_params, x);
5127 <<k model utility includes>>=
5130 #include <string.h> /* strlen() */
5131 #include <assert.h> /* assert() */
5134 #include "string_eval.h"
5135 #include "k_model.h"
5138 <<k model utility definitions>>=
5139 <<version definition>>
5140 #define VFLAG 1 /* verbose */
5141 enum mode_t {M_K_OF_F, M_SPECIAL};
5142 <<string comparison definition and globals>>
5143 <<k model getopt definitions>>
5144 <<initialize model definition>>
5145 <<kramers k structure>>
5146 <<kramers integ k structure>>
5150 <<k model utility getopt functions>>=
5152 <<k model utility help>>
5153 <<k model utility get options>>
5156 <<k model utility help>>=
5157 void help(char *prog_name,
5159 int n_k_models, k_model_getopt_t *k_models,
5160 int k_model, double Fmax, double special_xmin, double special_xmax)
5163 printf("usage: %s [options]\n", prog_name);
5164 printf("Version %s\n\n", VERSION);
5165 printf("A utility for understanding the available unfolding models\n\n");
5166 printf("Environmental options:\n");
5167 printf("-T T\tTemperature T (currently %g K)\n", env->T);
5168 printf("-C T\tYou can also set the temperature T in Celsius\n");
5169 printf("Model options:\n");
5170 printf("-k model\tTransition rate model (currently %s)\n",
5171 k_models[k_model].name);
5172 printf("-K args\tTransition rate model argument string (currently %s)\n",
5173 k_models[k_model].params);
5174 printf("There are two output modes. In standard mode, k(F) is printed\n");
5175 printf("For example:\n");
5176 printf(" #Force (N)\tk (% pop. per s)\n");
5177 printf(" 123.456\t7.89\n");
5179 printf("In special mode, the output depends on the model.\n");
5180 printf("For models defining an energy function E(x), that function is printed\n");
5181 printf(" #Distance (m)\tE (J)\n");
5182 printf(" 0.0012\t0.0034\n");
5184 printf("-m\tChange output to standard mode\n");
5185 printf("-M\tChange output to special mode\n");
5186 printf("-F\tSet the maximum F value for the standard mode k(F) output (currently %g)\n", Fmax);
5187 printf("-x\tSet the minimum x value for the special mode E(x) output (currently %g)\n", special_xmin);
5188 printf("-X\tSet the maximum x value for the special mode E(x) output (currently %g)\n", special_xmax);
5189 printf("-V\tChange output to verbose mode\n");
5190 printf("-h\tPrint this help and exit\n");
5192 printf("Unfolding rate models:\n");
5193 for (i=0; i<n_k_models; i++) {
5194 printf("\t%s\t%s\n", k_models[i].name, k_models[i].description);
5195 for (j=0; j < k_models[i].num_params ; j++)
5196 printf("\t\t\t%s\n", k_models[i].param_descriptions[j]);
5197 printf("\t\tdefaults: %s\n", k_models[i].params);
5202 <<k model utility get options>>=
5203 void get_options(int argc, char **argv, environment_t *env,
5204 int n_k_models, k_model_getopt_t *k_models,
5205 enum mode_t *mode, k_model_getopt_t **model,
5206 double *Fmax, double *special_xmin, double *special_xmax,
5207 unsigned int *flags)
5209 char *prog_name = NULL;
5210 char c, options[] = "T:C:k:K:mMF:x:X:Vh";
5212 extern char *optarg;
5213 extern int optind, optopt, opterr;
5217 /* setup defaults */
5219 prog_name = argv[0];
5220 env->T = 300.0; /* K */
5225 *special_xmax = 1e-8;
5226 *special_xmin = 0.0;
5228 while ((c=getopt(argc, argv, options)) != -1) {
5230 case 'T': env->T = atof(optarg); break;
5231 case 'C': env->T = atof(optarg)+273.15; break;
5233 k_model = index_k_model(n_k_models, k_models, optarg);
5234 *model = k_models+k_model;
5237 k_models[k_model].params = optarg;
5239 case 'm': *mode = M_K_OF_F; break;
5240 case 'M': *mode = M_SPECIAL; break;
5241 case 'F': *Fmax = atof(optarg); break;
5242 case 'x': *special_xmin = atof(optarg); break;
5243 case 'X': *special_xmax = atof(optarg); break;
5244 case 'V': *flags |= VFLAG; break;
5246 fprintf(stderr, "unrecognized option '%c'\n", optopt);
5247 /* fall through to default case */
5249 help(prog_name, env, n_k_models, k_models, k_model, *Fmax, *special_xmin, *special_xmax);
5258 \section{\LaTeX\ documentation}
5260 The comment blocks in this [[noweb]] file are in \LaTeX\ with \BibTeX\ citations.
5261 The comment blocks are extracted (with nicely formatted code blocks), using
5262 <<latex makefile lines>>=
5263 $(BUILD_DIR)/sawsim.tex : $(SRC_DIR)/sawsim.nw | $(BUILD_DIR)
5264 noweave -latex -index -delay $< > $@
5265 $(BUILD_DIR)/sawsim.bib : $(SRC_DIR)/sawsim.bib | $(BUILD_DIR)
5269 <<latex makefile lines>>=
5270 $(DOC_DIR)/sawsim.pdf : $(BUILD_DIR)/sawsim.tex $(BUILD_DIR)/sawsim.bib \
5272 $(SHELL) -e -c "cd $(BUILD_DIR) && pdflatex sawsim"
5273 $(SHELL) -e -c "cd $(BUILD_DIR) && bibtex sawsim"
5274 $(SHELL) -e -c "cd $(BUILD_DIR) && pdflatex sawsim"
5275 $(SHELL) -e -c "cd $(BUILD_DIR) && pdflatex sawsim"
5276 mv $(BUILD_DIR)/sawsim.pdf $@
5278 The first [[pdflatex]] pass reads through the \TeX\ source and extracts citation information,
5279 the \BibTeX\ pass looks through the \BibTeX\ databases for the cited works, and
5280 the remaining two [[pdflatex]] passes insert the citations and finalize the various indices and numberings.
5282 [[bibtex]] and [[pdflatex]] produce quite a few intermediate files, so provide a means to clean them up.
5283 <<latex makefile lines>>=
5285 rm -f $(BUILD_DIR)/sawsim.aux $(BUILD_DIR)/sawsim.bbl \
5286 $(BUILD_DIR)/sawsim.blg $(BUILD_DIR)/sawsim.log \
5287 $(BUILD_DIR)/sawsim.out $(BUILD_DIR)/sawsim.tex \
5288 $(BUILD_DIR)/sawsim.bib
5289 clean_latex : semi-clean_latex
5290 rm -f $(DOC_DIR)/sawsim.pdf
5295 [[make]] is a common utility on *nix systems for managing dependencies while building software.
5296 In this case, we have several \emph{targets} that we'd like to build:
5297 the main simulation program \prog;
5298 the testing programs [[check_sawsim]], [[check_tension_balance]], and [[check_list]];
5299 the documentation [[sawsim.pdf]];
5300 and the [[Makefile]] itself.
5301 Besides the generated files, there is the utility target
5302 [[clean]] for removing all generated files except [[Makefile]].
5304 % [[dist]] for generating a distributable tar file.
5306 Extract the makefile with
5307 `[[notangle -Rmakefile src/sawsim.nw | sed 's/ /\t/' > Makefile]]'.
5308 The sed is needed because notangle replaces tabs with eight spaces,
5309 but [[make]] needs tabs.
5311 # Decide what directories to put things in
5316 # Note: Cannot use, for example, `./build', because make eats the `./'
5317 # and then $(BUILD_DIR) doesn't match in $$(subst $(BUILD_DIR), ...)
5319 # Modules (X.c, X.h) defined in the noweb file
5322 # Modules defined outside the noweb file
5323 FREE_SAWSIM_MODS = interp tavl
5325 <<list module makefile lines>>
5326 <<tension balance module makefile lines>>
5327 <<tension model module makefile lines>>
5328 <<k model module makefile lines>>
5329 <<parse module makefile lines>>
5330 <<string eval module makefile lines>>
5331 <<accel k module makefile lines>>
5333 SAWSIM_MODS = $(NW_SAWSIM_MODS) $(FREE_SAWSIM_MODS)
5335 # Everything needed for sawsim under one roof. sawsim.c must come first
5336 SAWSIM_SRC = $(BUILD_DIR)/sawsim.c $(BUILD_DIR)/global.h \
5337 $(SAWSIM_MODS:%=$(BUILD_DIR)/%.c) $(SAWSIM_MODS:%=$(BUILD_DIR)/%.h)
5338 # Libraries needed to compile sawsim
5339 LIBS = gsl gslcblas m
5340 CHECK_LIBS = gsl gslcblas m check
5341 # The non-check binaries generated
5342 BINS = sawsim tension_model_utils k_model_utils sawsim_profile
5345 # Define the major targets
5346 all : ./Makefile $(BINS:%=$(BIN_DIR)/%) $(DOCS:%=$(DOC_DIR)/%) ;
5348 view : $(DOC_DIR)/sawsim.pdf
5350 profile : $(BIN_DIR)/sawsim_profile | $(BIN_DIR)
5351 $(BIN_DIR)/sawsim_profile -v1e-6 -Mhooke -A.05 -U1 -kkramers_integ \
5352 -mnull -Mwlc -A0.39e-9,28e-9 -F8
5353 gprof $(BIN_DIR)/sawsim_profile gmon.out > $@
5354 check : $(CHECK_BINS:%=$(BIN_DIR)/%) $(BIN_DIR)/check_sawsim
5355 $(SHELL) -e -c 'for B in $^; do ./$$B; done'
5356 clean : $(CHECK_BINS:%=clean_%) $(SAWSIM_MODS:%=clean_%) \
5357 clean_tension_model_utils \
5358 clean_k_model_utils clean_latex clean_check_sawsim
5359 rm -f $(BIN_DIR)/sawsim $(BIN_DIR)/sawsim_static \
5360 $(BIN_DIR)/sawsim_profile $(BUILD_DIR)/sawsim.c \
5361 $(BUILD_DIR)/interp.c $(BUILD_DIR)/interp.h \
5362 $(BUILD_DIR)/tavl.c $(BUILD_DIR)/tavl.h \
5363 $(BUILD_DIR)/global.h ./gmon.out
5364 $(SHELL) -e -c "rmdir $(BUILD_DIR) $(BIN_DIR) $(DOC_DIR)"
5366 # Various builds of sawsim
5367 $(BIN_DIR)/sawsim : $(SAWSIM_SRC) | $(BIN_DIR)
5368 gcc -g -o $@ $< $(SAWSIM_MODS:%=$(BUILD_DIR)/%.c) $(LIBS:%=-l%)
5369 $(BIN_DIR)/sawsim_static : $(SAWSIM_SRC) | $(BIN_DIR)
5370 gcc -g -static -o $@ $< $(SAWSIM_MODS:%=$(BUILD_DIR)/%.c) $(LIBS:%=-l%)
5371 $(BIN_DIR)/sawsim_profile : $(SAWSIM_SRC) | $(BIN_DIR)
5372 gcc -g -pg -o $@ $< $(SAWSIM_MODS:%=$(BUILD_DIR)/%.c) $(LIBS:%=-l%)
5374 # Create the directories
5375 $(BUILD_DIR) $(BIN_DIR) $(DOC_DIR) :
5378 # Copy over the source external to sawsim
5379 # Note: Cannot use, for example, `./build', because make eats the `./'
5380 # and then $(BUILD_DIR) doesn't match in $$(subst $(BUILD_DIR), ...)
5382 $(FREE_SAWSIM_MODS:%=$(BUILD_DIR)/%.h) : $$(subst $(BUILD_DIR),$(SRC_DIR),$$@)\
5386 $(FREE_SAWSIM_MODS:%=$(BUILD_DIR)/%.c) : $$(subst $(BUILD_DIR),$(SRC_DIR),$$@)\
5390 ## Basic source generated with noweb
5391 # The central files sawsim.c and global.h...
5392 $(BUILD_DIR)/sawsim.c : $(SRC_DIR)/sawsim.nw | $(BUILD_DIR)
5394 $(BUILD_DIR)/global.h : $(SRC_DIR)/sawsim.nw | $(BUILD_DIR)
5395 notangle -Rglobal.h $< > $@
5396 # ... and the modules
5397 $(NW_SAWSIM_MODS:%=$(BUILD_DIR)/%.h) : $(SRC_DIR)/sawsim.nw | $(BUILD_DIR)
5398 notangle -R$(subst _,-,$(@:$(BUILD_DIR)/%=%)) $< > $@
5399 $(NW_SAWSIM_MODS:%=$(BUILD_DIR)/%.c) : $(SRC_DIR)/sawsim.nw | $(BUILD_DIR)
5400 notangle -R$(subst _,-,$(@:$(BUILD_DIR)/%=%)) $< > $@
5401 $(CHECK_BINS:%=$(BUILD_DIR)/%.c) : $(SRC_DIR)/sawsim.nw | $(BUILD_DIR)
5402 notangle -R$(subst _,-,$(@:$(BUILD_DIR)/%=%)) $< > $@
5403 # Note: I use `_' as a space in my file names, but noweb doesn't like
5404 # that so we use `-' to name the noweb roots and substitute here.
5406 ## Building the unit-test programs
5408 $(BUILD_DIR)/check_sawsim.c : $(SRC_DIR)/sawsim.nw
5409 notangle -Rchecks $< > $@
5410 $(BIN_DIR)/check_sawsim : $(BUILD_DIR)/check_sawsim.c $(BUILD_DIR)/global.h \
5411 $(SAWSIM_MODS:%=$(BUILD_DIR)/%.c) \
5412 $(SAWSIM_MODS:%=$(BUILD_DIR)/%.h) | $(BIN_DIR)
5413 gcc -g -o $@ $< $(SAWSIM_MODS:%=$(BUILD_DIR)/%.c) $(CHECK_LIBS:%=-l%)
5414 clean_check_sawsim :
5415 rm -f $(BIN_DIR)/check_sawsim $(BUILD_DIR)/check_sawsim.c
5416 # ... and the modules
5418 $(CHECK_BINS:%=$(BIN_DIR)/%) : $$(subst $(BIN_DIR),$(BUILD_DIR),$$@).c \
5419 $$(subst $(BIN_DIR)/check_,$(BUILD_DIR)/,$$@).c \
5420 $$(subst $(BIN_DIR)/check_,$(BUILD_DIR)/,$$@).h \
5421 $$(patsubst %,$(BUILD_DIR)/%.c,$$($$(subst $(BIN_DIR)/,,$$@_MODS)))\
5422 $$(patsubst %,$(BUILD_DIR)/%.h,$$($$(subst $(BIN_DIR)/,,$$@_MODS)))\
5423 $$(BUILD_DIR)/global.h | $(BIN_DIR)
5424 gcc -g -o $@ $< $(@:$(BIN_DIR)/check_%=$(BUILD_DIR)/%.c) \
5425 $(patsubst %,$(BUILD_DIR)/%.c,$($(subst $(BIN_DIR)/,,$@_MODS)))\
5427 # todo: clean up the required modules to
5428 $(CHECK_BINS:%=clean_%) :
5429 rm -f $(@:clean_%=$(BIN_DIR)/%) $(@:clean_%=$(BUILD_DIR)/%.c)
5431 # Cleaning up the modules
5433 $(SAWSIM_MODS:%=clean_%) :
5434 rm -f $(@:clean_%=$(BUILD_DIR)/%.c) $(@:clean_%=$(BUILD_DIR)/%.h)
5436 <<tension model utils makefile lines>>
5437 <<k model utils makefile lines>>
5438 <<latex makefile lines>>
5440 Makefile : $(SRC_DIR)/sawsim.nw
5441 notangle -Rmakefile $< | sed 's/ /\t/' > $@
5443 This is fairly self-explanatory. We compile a dynamically linked
5444 version ([[sawsim]]) and a statically linked version
5445 ([[sawsim_static]]). The static version is about 9 times as big, but
5446 it runs without needing dynamic GSL libraries to link to, so it's a
5447 better format for distributing to a cluster for parallel evaluation.
5451 \subsection{Hookean springs in series}
5452 \label{app.math_hooke}
5455 The effective spring constant for $n$ springs $k_i$ in series is given by
5457 k_\text{eff} = \p({\sum_{i \le n}\frac{1}{k_i}})^{-1}.
5463 F &= k_i x_i &&\forall i \le n \\
5464 x_i &= \frac{k_j}{k_i} x_j &&\forall i,j \le n \\
5465 x &= \sum_i x_i = x_1 \sum_i \frac{k_1}{k_i} \\
5466 F &= k_1 x_1 = k_\text{eff} x \\
5467 k_\text{eff} &= k_1 \frac{x_1}{x} = \frac{k_1}{\sum_i \frac{k_1}{k_i}}
5468 = \p({\sum_{i \le n}\frac{1}{k_i}})^{-1}.
5473 \addcontentsline{toc}{section}{References}
5474 \bibliography{sawsim}