From 5c2915f00d9ac1f0b0189fe2d282f44b40ebc565 Mon Sep 17 00:00:00 2001 From: thomie Date: Thu, 16 Dec 2010 17:43:42 +0100 Subject: [PATCH 0001/1541] Minor changes Better output for Singles --- single.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/single.py b/single.py index e684fff8..03cffd9e 100644 --- a/single.py +++ b/single.py @@ -160,7 +160,8 @@ def __str__(self): name = self.world.model.get_species_type_by_id(sid)["name"] if name[0] != '(': name = '(' + name + ')' - return 'Single[%s: %s, ST%s]' % (self.domain_id, pid, name) + return 'Single[%s: %s, ST%s, %s]' % (self.domain_id, pid, name, + self.pid_particle_pair[1].position) class NonInteractionSingle(Single): From 5f4ff8baf44ee1179d9001ab81252be5e4c404b4 Mon Sep 17 00:00:00 2001 From: thomie Date: Mon, 22 Nov 2010 15:26:01 +0100 Subject: [PATCH 0002/1541] Minor tweaks visualization/pipeline.py Add annotate time filter to Paraview pipeline Turn Paraview pipeline constants into variables --- visualization/pipeline.py | 41 ++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/visualization/pipeline.py b/visualization/pipeline.py index c33c092f..9e7fc148 100644 --- a/visualization/pipeline.py +++ b/visualization/pipeline.py @@ -20,6 +20,8 @@ DARK_BACKGROUND = True PARTICLE_RADIUS_SCALE_FACTOR = 1 +SPHERE_RADIUS_SCALE_FACTOR = 1 +CYLINDER_RADIUS_SCALE_FACTOR = 1 HELIX_RADIUS_SCALE_FACTOR = 1 RESOLUTION = 18 @@ -122,12 +124,19 @@ def __init__(self, egfrd_directory, simulation_data_directory): path of the directory that contains the files 'files.pvd' and 'static.pvd', as created by VTKLogger. + Call p.rebuild() after this. + """ self.paraview_scripts_directory = egfrd_directory + '/visualization/' self.simulation_data_directory = simulation_data_directory + self.particle_radius_scale_factor = PARTICLE_RADIUS_SCALE_FACTOR + self.sphere_radius_scale_factor = SPHERE_RADIUS_SCALE_FACTOR + self.cylinder_radius_scale_factor = CYLINDER_RADIUS_SCALE_FACTOR + self.helix_radius_scale_factor = HELIX_RADIUS_SCALE_FACTOR + self.resolution = RESOLUTION + self.initialize() - self.rebuild() def initialize(self): dir = self.paraview_scripts_directory @@ -284,12 +293,19 @@ def rebuild(self): static = self.add_pvd_reader(static_pvd_path, 'static.pvd') + if version > 4: + self.annTime = self.simple.AnnotateTimeFilter(files) + self.annTime.Format = '%.2f s' + rep = self.show(self.annTime) + #rep.WindowLocation + rep.FontSize = 12 if PARTICLES: particle_data = self.add_extract_block(files, [2], 'b1') - particles = self.add_sphere_glyph(particle_data, name='Particles') - particles.SetScaleFactor = PARTICLE_RADIUS_SCALE_FACTOR + particles = self.add_sphere_glyph(particle_data, self.resolution, + name='Particles') + particles.SetScaleFactor = self.particle_radius_scale_factor rep1 = self.show(particles) self.set_color(particles, rep1) @@ -298,8 +314,9 @@ def rebuild(self): if SPHERES: sphere_data = self.add_extract_block(files, [4], 'b2') - spheres = self.add_sphere_glyph(sphere_data, RESOLUTION, + spheres = self.add_sphere_glyph(sphere_data, self.resolution, name='Spheres') + spheres.SetScaleFactor = self.sphere_radius_scale_factor rep2 = self.show(spheres) self.set_color(spheres, rep2, color_map=MakeNiceLT) @@ -309,9 +326,11 @@ def rebuild(self): if CYLINDERS: cylinder_data = self.add_extract_block(files, [6], 'b3') - + scale = self.cylinder_radius_scale_factor cylinders = self.add_tensor_glyph(cylinder_data, 'Cylinder', - resolution=RESOLUTION, name='tg') + resolution=self.resolution, + name='tg', + scale=scale) programmable_filter = self.add_color_hack(cylinders, name='Cylinders') @@ -330,7 +349,7 @@ def rebuild(self): cylindrical_surfaces = \ self.add_tensor_glyph(cylindrical_surface_data, 'Cylinder', name='Cylindrical Surfaces', - scale=HELIX_RADIUS_SCALE_FACTOR) + scale=self.helix_radius_scale_factor) rep4 = self.show(cylindrical_surfaces) rep4.Representation = 'Wireframe' @@ -342,7 +361,7 @@ def rebuild(self): helix_file = open(helix_path, 'r') helix_source = servermanager.sources.ProgrammableSource() helix_source.Script = 'HELIX_RADIUS_SCALE_FACTOR = ' + \ - str(HELIX_RADIUS_SCALE_FACTOR) + \ + str(self.helix_radius_scale_factor) + \ '\n' + helix_file.read() helix_source.UpdatePipeline() servermanager.Register(helix_source, registrationName='ps') @@ -378,15 +397,15 @@ def rebuild(self): if value > 0 and value < cylindrical_surface_radius: cylindrical_surface_radius = value - helix_radius = HELIX_RADIUS_SCALE_FACTOR * \ + helix_radius = self.helix_radius_scale_factor * \ cylindrical_surface_radius # Make double_helix a bit thinner than helix. double_helix.Radius = helix_radius / 20 rep4 = self.show(double_helix) - self.set_color(double_helix, rep4, - color_array_name = 'TubeNormals') + self.set_color(double_helix, rep4, color_map=MakeCoolToWarmLT, + color_array_name='TubeNormals') # Planar surfaces. From 587c57ab65d71301a3371b8c6de4727f27032339 Mon Sep 17 00:00:00 2001 From: thomie Date: Tue, 9 Nov 2010 18:34:25 +0100 Subject: [PATCH 0003/1541] Refactor single.py Nothing major, just reshuffling a bit for Interactions later. --- single.py | 59 ++++++++++++++++++++++--------------------------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/single.py b/single.py index 03cffd9e..ab5c588a 100644 --- a/single.py +++ b/single.py @@ -21,8 +21,7 @@ class Single(object): specified. """ - def __init__(self, domain_id, pid_particle_pair, shell_id, reactiontypes, - surface): + def __init__(self, domain_id, pid_particle_pair, reactiontypes, surface): self.multiplicity = 1 self.num_shells = 1 @@ -37,12 +36,6 @@ def __init__(self, domain_id, pid_particle_pair, shell_id, reactiontypes, self.surface = surface - # Create shell. - shell = self.create_new_shell(pid_particle_pair[1].position, - pid_particle_pair[1].radius, domain_id) - - self.shell_list = [(shell_id, shell), ] - self.event_id = None self.domain_id = domain_id @@ -73,6 +66,12 @@ def set_shell_id_shell_pair(self, value): shell_id_shell_pair = property(get_shell_id_shell_pair, set_shell_id_shell_pair) + def get_mobility_radius(self): + return self.get_shell_size() - self.pid_particle_pair[1].radius + + def get_shell_size(self): + return self.shell_list[0][1].shape.radius + def initialize(self, t): ''' Reset the Single. @@ -100,21 +99,11 @@ def draw_reaction_time_tuple(self): dt = (1.0 / self.k_tot) * math.log(1.0 / myrandom.uniform()) return dt, EventType.SINGLE_REACTION - def draw_interaction_time(self): - """Todo. - - Note: we are not calling single.drawEventType() just yet, but - postpone it to the very last minute (when this event is executed - in fire_single). So IV_EVENT can still be an iv escape or an iv - interaction. - - """ - pass + def draw_escape_time_tuple(self): + """Return an (escape time, event type)-tuple. - def draw_escape_or_interaction_time_tuple(self): - """Return an (escape or interaction time, event type)-tuple. - - Handles also all interaction events. + In case of an interaction single, does not handle escapes in + the interaction coordinate. """ if self.getD() == 0: @@ -125,13 +114,6 @@ def draw_escape_or_interaction_time_tuple(self): event_type = EventType.SINGLE_ESCAPE return dt, event_type - def determine_next_event(self): - """Return an (event time, event type)-tuple. - - """ - return min(self.draw_escape_or_interaction_time_tuple(), - self.draw_reaction_time_tuple()) - def updatek_tot(self): self.k_tot = 0 @@ -175,14 +157,21 @@ class NonInteractionSingle(Single): """ def __init__(self, domain_id, pid_particle_pair, shell_id, reactiontypes, surface): - Single.__init__(self, domain_id, pid_particle_pair, shell_id, - reactiontypes, surface) + Single.__init__(self, domain_id, pid_particle_pair, reactiontypes, + surface) - def get_mobility_radius(self): - return self.get_shell_size() - self.pid_particle_pair[1].radius + # Create shell. + shell = self.create_new_shell(pid_particle_pair[1].position, + pid_particle_pair[1].radius, domain_id) - def get_shell_size(self): - return self.shell_list[0][1].shape.radius + self.shell_list = [(shell_id, shell), ] + + def determine_next_event(self): + """Return an (event time, event type)-tuple. + + """ + return min(self.draw_escape_time_tuple(), + self.draw_reaction_time_tuple()) def draw_new_position(self, dt, event_type): if event_type == EventType.SINGLE_ESCAPE: From 63ee93652525d6cb8b8321f1c52aa239a5b98502 Mon Sep 17 00:00:00 2001 From: thomie Date: Tue, 26 Oct 2010 11:17:57 +0200 Subject: [PATCH 0004/1541] Add stubs + docstrings to model for surface binding --- model.py | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/model.py b/model.py index 59dc00f0..86ac16fe 100644 --- a/model.py +++ b/model.py @@ -11,6 +11,9 @@ 'create_annihilation_reaction_rule', 'create_binding_reaction_rule', 'create_unbinding_reaction_rule', + 'create_surface_absorption_reaction_rule', + 'create_surface_binding_reaction_rule', + 'create_surface_unbinding_reaction_rule', # From _gfrd. Should be part of the model class. 'create_cuboidal_region', @@ -88,6 +91,9 @@ Surfaces are not allowed to touch or overlap. +Todo: allow the user to specify the position of a planar surface +relative to a region. + """ _gfrd.Model.add_species_type.im_func.__doc__ = \ @@ -364,3 +370,104 @@ def create_unbinding_reaction_rule(reactant, product1, product2, kd): rr['k'] = '%.16g' % kd return rr +def create_surface_absorption_reaction_rule(reactant, surface, ka): + """Example: A + some_surface -> 0 + some_surface + + Arguments: + - reactant + a Species in the "world" or in a Region. + - surface + a Surface created with one of the functions + model.create_<>_surface. + - ka + intrinsic reaction rate. Units: meters^3 per second. (Rough + order of magnitude: 1e-16 m^3/s to 1e-20 m^3/s) + + ka should be an *intrinsic* reaction rate. No analytical expression + is currently known to convert an overall reaction rate (kon) to + an intrinsic reaction rate (ka) for surface binding or absorption + reaction rules. + + By default an EGFRDSimulator will assume a repulsive + surface binding reaction rule (ka=0) for each possible + combination of reactant and Surface for which no surface + binding or absorption reaction rule is specified. You can + explicitly add these reaction rules to the model by calling + model.ParticleModel.set_all_repulsive(). + + """ + assert isinstance(reactant['structure'], _gfrd.BoxShapedRegion) + +def create_surface_binding_reaction_rule(reactant, surface, product, ka): + """Example: A + some_surface -> A_on_surface + some_surface + + Arguments: + - reactant + a Species in the "world" or in a Region. + - surface + a Surface created with one of the functions + model.create_<>_surface. + - product + a Species that can exist on the Surface mentioned in the + previous argument. For example: if you assigned that + Surface to the variable some_surface, then a valid product + Species would be: + model.Species('A_on_surface', some_D, some_r, some_surface) + - ka + intrinsic reaction rate. Units: meters^3 per second. (Rough + order of magnitude: 1e-16 m^3/s to 1e-20 m^3/s) + + A surface binding reaction rule always has exactly one product. + + ka should be an *intrinsic* reaction rate. No analytical expression + is currently known to convert an overall reaction rate (kon) to + an intrinsic reaction rate (ka) for surface binding or absorption + reaction rules. + + By default an EGFRDSimulator will assume a repulsive + surface binding reaction rule (ka=0) for each possible + combination of reactant and Surface for which no surface + binding or absorption reaction rule is specified. You can + explicitly add these reaction rules to the model by calling + model.ParticleModel.set_all_repulsive(). + + """ + assert product['structure'] == surface + +def create_surface_unbinding_reaction_rule(reactant, surface, product, kd): + """Example: A_on_surface + some_surface -> A + some_surface + + Arguments: + - reactant + a Species that can exist on the Surface mentioned in the + next argument. For example: if you assign that + Surface to the variable some_surface, then a valid reactant + Species would be: + model.Species('A_on_surface', some_D, some_r, some_surface) + - surface + a Surface created with one of the functions + model.create_<>_surface. + - product + a Species in the "world" or in a Region. + - kd + intrinsic reaction rate. Units: per second. (Rough order of + magnitude: 1e-2 /s to 1e2 /s). + + A surface unbinding reaction rule always has exactly one product. + + kd should be an *intrinsic* reaction rate. No analytical expression + is currently known to convert an overall reaction rate (koff) to + an intrinsic reaction rate (kd) for surface unbinding reaction rules. + + A surface unbinding reaction rule defines a Poissonian process. + + """ + assert reactant['structure'] == surface + +def create_membrane_traversal_reaction_rule(reactant, surface1, + product, surface2, ka): + """ + + """ + # Implementation: also flip orientation of cylinder. + pass From 3478bacc2ad8441a6552b9478923b32b95937962 Mon Sep 17 00:00:00 2001 From: thomie Date: Tue, 26 Oct 2010 16:18:31 +0200 Subject: [PATCH 0005/1541] Change fire_single to try interactions also --- egfrd.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/egfrd.py b/egfrd.py index b3abadcf..b58f414c 100644 --- a/egfrd.py +++ b/egfrd.py @@ -21,6 +21,7 @@ CuboidalRegion, CylindricalSurface, PlanarSurface, + Surface, _random_vector, Sphere, NetworkRulesWrapper, @@ -736,6 +737,7 @@ def fire_single(self, single): min_shell = single.pid_particle_pair[1].radius * \ self.SINGLE_SHELL_FACTOR + # Burst intruders. intruders, closest, closest_distance = \ self.get_intruders(singlepos, min_shell, ignore=[single.domain_id, ]) @@ -747,7 +749,25 @@ def fire_single(self, single): if intruders: burst = self.burst_non_multis(intruders) + else: + burst = None + + # Check closest surface. + # Only relevant when in the "world" or other 3D region. + if isinstance(single.surface, CuboidalRegion): + interaction_surface, closest_surface, surface_distance = \ + get_closest_surface_within_radius(self.world, + singlepos, min_shell, + ignore=[single.surface.id]) + if interaction_surface: + obj = self.form_interaction(single, interaction_surface, burst) + if obj: + return + else: + # Surfaces are not allowed to touch or overlap. + closest_surface = None + if burst: obj = self.form_pair_or_multi(single, burst) if obj: @@ -777,6 +797,9 @@ def fire_single(self, single): FORMAT_DOUBLE % s.dt, closest, FORMAT_DOUBLE % closest_distance)) else: + if closest_surface and surface_distance < closest_distance: + closest = closest_surface + closest_distance = surface_distance self.update_single(single, closest, closest_distance) if __debug__: @@ -1133,6 +1156,11 @@ def form_pair_or_multi(self, single, neighbors): if obj: return obj + def form_interaction(self, single, surface, burst): + if __debug__: + log.debug('trying to form Interaction(%s, %s)' % + (single.pid_particle_pair, surface)) + raise NotImplementedError def form_pair(self, single1, pos1, single2, burst): if __debug__: @@ -1142,6 +1170,14 @@ def form_pair(self, single1, pos1, single2, burst): assert single1.is_reset() assert single2.is_reset() + # Try forming a Pair only if singles are on same surface. + if single1.surface != single2.surface: + if __debug__: + log.debug('Pair(%s, %s) not formed: not on same surface.' % + (single1.pid_particle_pair[0], + single2.pid_particle_pair[0])) + return None + # 1. Determine min shell size. radius1 = single1.pid_particle_pair[1].radius radius2 = single2.pid_particle_pair[1].radius @@ -1258,7 +1294,7 @@ def form_pair(self, single1, pos1, single2, burst): assert shell_size < closest_shell_distance else: - assert isinstance(closest, (Pair, Multi, None.__class__)) + assert isinstance(closest, (Pair, Multi, Surface, None.__class__)) shell_size = closest_shell_distance / SAFETY From cf64b1d1d69581942e4c3f1640118b8fa627a240 Mon Sep 17 00:00:00 2001 From: thomie Date: Fri, 12 Nov 2010 17:38:07 +0100 Subject: [PATCH 0006/1541] Implement creation of InteractionSingles Miedema's algorithm. --- egfrd.py | 272 +++++++++++++++++++++++++++++++++++++++++++++++++++++- single.py | 158 +++++++++++++++++++++++++++++++ 2 files changed, 427 insertions(+), 3 deletions(-) diff --git a/egfrd.py b/egfrd.py index b58f414c..abec1869 100644 --- a/egfrd.py +++ b/egfrd.py @@ -56,6 +56,25 @@ def create_default_single(domain_id, pid_particle_pair, shell_id, rt, surface): return PlanarSurfaceSingle(domain_id, pid_particle_pair, shell_id, rt, surface) +def create_default_interaction(domain_id, pid_particle_pair, shell_id, + reaction_types, interaction_type, surface, + projected_point, particle_distance, + orientation_vector, dr, dz_left, dz_right): + if isinstance(surface, CylindricalSurface): + return CylindricalSurfaceInteraction(domain_id, pid_particle_pair, + shell_id, reaction_types, + interaction_type, surface, + projected_point, particle_distance, + orientation_vector, + dr, dz_left, dz_right) + elif isinstance(surface, PlanarSurface): + return PlanarSurfaceInteraction(domain_id, pid_particle_pair, + shell_id, reaction_types, + interaction_type, surface, + projected_point, particle_distance, + orientation_vector, + dr, dz_left, dz_right) + def create_default_pair(domain_id, com, single1, single2, shell_id, r0, shell_size, rt, surface): if isinstance(surface, CuboidalRegion): @@ -327,6 +346,40 @@ def create_single(self, pid_particle_pair): single.world = self.world return single + def create_interaction(self, single, surface, projected_point, + particle_distance, orientation_vector, + dr, dz_left, dz_right): + assert single.dt == 0.0 and single.get_mobility_radius() == 0.0 + + pid_particle_pair = single.pid_particle_pair + species_id = pid_particle_pair[1].sid + + reaction_types = self.network_rules.query_reaction_rule(species_id) + #interaction_type = self.query_interaction_rule(species_id, surface) + # TODO. + interaction_type = None + + domain_id = self.domain_id_generator() + shell_id = self.shell_id_generator() + + interaction = \ + create_default_interaction(domain_id, pid_particle_pair, shell_id, + reaction_types, interaction_type, + surface, projected_point, + particle_distance, orientation_vector, + dr, dz_left, dz_right) + interaction.initialize(self.t) + + self.move_shell(interaction.shell_id_shell_pair) + self.domains[domain_id] = interaction + + if __debug__: + # Used in __str__. + interaction.world = self.world + + assert isinstance(interaction, InteractionSingle) + return interaction + def create_pair(self, single1, single2, com, r0, shell_size): assert single1.dt == 0.0 assert single2.dt == 0.0 @@ -1157,10 +1210,223 @@ def form_pair_or_multi(self, single, neighbors): return obj def form_interaction(self, single, surface, burst): + # Try to form an interaction. + + particle = single.pid_particle_pair[1] + # Cyclic transpose needed when calling surface.projected_point! + pos_transposed = self.world.cyclic_transpose(particle.position, + surface.shape.position) + projected_point, projection_distance = \ + surface.projected_point(pos_transposed) + + particle_distance = abs(projection_distance) + + # For interaction with a planar surface, decide orientation. + orientation_vector = cmp(projection_distance, 0) * surface.shape.unit_z + if __debug__: - log.debug('trying to form Interaction(%s, %s)' % - (single.pid_particle_pair, surface)) - raise NotImplementedError + log.debug('trying to form Interaction(%s, %s)' % (particle, surface)) + + # For an interaction with a PlanarSurface (membrane): + # * dr is the radius of the cylinder. + # * dz_left determines how much the cylinder is sticking out on the + # other side of the surface, measured from the projected point. + # * dz_right is the distance between the particle position and + # the edge of the cylinder in the z direction. + # + # For interaction with a CylindricalSurface (dna): + # * dr is the distance between the particle position and the + # edge of the cylinder in the r direction. + # * dz_left is the distance from the projected point to the left edge + # of the cylinder. + # * dz_right is the distance from the projected point to the right edge + # of the cylinder. + + # Initialize dr, dz_left, dz_right to maximum allowed values. + # And decide minimal dr, dz_left, dz_right. + if isinstance(surface, PlanarSurface): + dr = self.get_max_shell_size() + # Leave enough for the particle itself to the left. + dz_left = particle.radius + # Make sure the cylinder stays within 1 cell. + dz_right = self.get_max_shell_size() * 2 - \ + (particle_distance + dz_left) + + min_dr = particle.radius * self.SINGLE_SHELL_FACTOR + min_dz_left = dz_left + min_dz_right = particle.radius * self.SINGLE_SHELL_FACTOR + + elif isinstance(surface, CylindricalSurface): + # Make sure the cylinder stays within 1 cell. + dr = self.get_max_shell_size() - particle_distance + dz_left = self.get_max_shell_size() + dz_right = self.get_max_shell_size() + + min_dr = particle.radius * self.SINGLE_SHELL_FACTOR + min_dz_left = particle.radius * self.SINGLE_SHELL_FACTOR + min_dz_right = particle.radius * self.SINGLE_SHELL_FACTOR + + # Miedema's algorithm. + dr, dz_left, dz_right = \ + self.determine_optimal_cylinder(single, surface, + projected_point, + particle_distance, + orientation_vector, + dr, dz_left, dz_right, + burst) + dr /= SAFETY + dz_left /= SAFETY + dz_right /= SAFETY + + # Decide if interaction domain is possible. + if dr < min_dr or dz_left < min_dz_left or dz_right < min_dz_right: + if __debug__: + log.debug(' *Interaction not possible:\n' + ' %s +\n' + ' %s.\n' + ' dr = %.3g. min_dr = %.3g.\n' + ' dz_left = %.3g. min_dz_left = %.3g.\n' + ' dz_right = %.3g. min_dz_right = %.3g.' % + (single, surface, dr, min_dr, dz_left, min_dz_left, + dz_right, min_dz_right)) + return None + + interaction = self.create_interaction(single, surface, + projected_point, + particle_distance, + orientation_vector, + dr, dz_left, dz_right) + + # Below here similar as in fire_pair after creating a pair. + interaction.dt, interaction.event_type, = \ + interaction.determine_next_event() + assert interaction.dt >= 0 + + self.last_time = self.t + + self.remove_domain(single) + # single event will be removed by the scheduler. + + if __debug__: + log.debug(' *create_interaction\n' + ' dr = %s. dz_left = %s. dz_right = %s.\n' % + (FORMAT_DOUBLE % dr, FORMAT_DOUBLE % dz_left, + FORMAT_DOUBLE % dz_right)) + + assert self.check_obj(interaction) + self.add_single_event(interaction) + + return interaction + + def determine_optimal_cylinder(self, single, surface, projected_point, + particle_distance, orientation_vector, + dr, dz_left, dz_right, burst): + # Find optimal cylinder around particle and surface, such that + # it is not interfering with other shells. + # Todo. Finding all spheres and cylinders in the matrixspace + # should be sufficient, but domain information is now used for + # get_shell_size is overridden for cylindrical singles and + # pairs. + all_neighbors = \ + self.get_neighbors_within_radius_no_sort(projected_point, + self.get_max_shell_size(), + ignore=[single.domain_id]) + + for domain in all_neighbors: + if isinstance(domain, Multi): + for shell in domain.shell_list: + shell_position = shell.shape.position + shell_size = shell.shape.radius + dr, dz_left, dz_right = \ + self.miedema_algorithm(shell_position, shell_size, + projected_point, + orientation_vector, dr, + dz_left, dz_right, + surface, particle_distance) + else: + # Get shell size, which normally is the radius of the + # shell, but in case of a cylinder around the same + # CylindricalSurface it should be the half_length of + # the cylinder. + shell_position = domain.shell.shape.position + shell_size = domain.get_shell_size() + + # Make bursted singles look bigger, like in form_pair, + # because the size of their shell is only + # particle.radius (not yet multiplied by + # SINGLE_SHELL_FACTOR) + # (and no we can not do that immediately after they are + # bursted, singles might start overlapping). + if domain.dt == 0.0 and domain.getD() > 0: + # This is one of the bursted singles. + # Or a particle that just escaped it's multi. + shell_size *= self.SINGLE_SHELL_FACTOR + + dr, dz_left, dz_right = \ + self.miedema_algorithm(shell_position, shell_size, + projected_point, + orientation_vector, dr, + dz_left, dz_right, surface, + particle_distance) + + + return dr, dz_left, dz_right + + def miedema_algorithm(self, shell_position, shell_size, projected_point, + orientation_vector, dr, dz_left, dz_right, + surface, particle_distance): + # Find optimal cylinder around particle and surface, such that + # it does not interfere with the shell at position + # shell_position and with size (radius or half_length) + # shell_size. + + shell_vector = shell_position - projected_point + + # Calculate zi and ri for this shell. + zi = numpy.dot(shell_vector, orientation_vector) + z_vector = zi * numpy.array(orientation_vector) + r_vector = numpy.array(shell_vector) - numpy.array(z_vector) + ri = numpy.linalg.norm(r_vector) + + # Calculate dr_i for this shell. + dr_i = ri - shell_size + if isinstance(surface, CylindricalSurface): + # Run Miedema's algorithm in the r direction + # relative to the particle's position, not to + # projected point (r=0). + dr_i -= particle_distance + + # Calculate dz_left_i or dz_right_i (both will usually + # be positive values). + if zi < 0: + # Calculate dz_left_i for this shell. + dz_left_i = - zi - shell_size + + # Miedema's algorithm left side. + if dz_left_i < dz_left and dr_i < dr: + if dz_left_i > dr_i: + dz_left = dz_left_i + else: + dr = dr_i + else: + # Calculate dz_right_i for this shell. + dz_right_i = zi - shell_size + + if isinstance(surface, PlanarSurface): + # On the particle side (right side), run + # Miedema's algorithm in the z direction + # relative to the particle's position, not the + # projected point (z=0). + dz_right_i -= particle_distance + + # Miedema's algorithm right side. + if dz_right_i < dz_right and dr_i < dr: + if dz_right_i > dr_i: + dz_right = dz_right_i + else: + dr = dr_i + + return dr, dz_left, dz_right def form_pair(self, single1, pos1, single2, burst): if __debug__: diff --git a/single.py b/single.py index ab5c588a..cb2325a3 100644 --- a/single.py +++ b/single.py @@ -9,6 +9,9 @@ 'PlanarSurfaceSingle', 'SphericalSingle', 'Single', + 'InteractionSingle', + 'CylindricalSurfaceInteraction', + 'PlanarSurfaceInteraction', ] class Single(object): @@ -314,3 +317,158 @@ def __str__(self): return 'CylindricalSurface' + Single.__str__(self) +class InteractionSingle(Single): + """Interactions singles are used when a particle is close to a + surface. + + There are 2 types of InteractionSingles: + * PlanarSurfaceInteraction. + * CylindricalSurfaceInteraction. + + """ + def __init__(self, domain_id, pid_particle_pair, shell_id, shell, + reactiontypes, interaction_type, surface): + + Single.__init__(self, domain_id, pid_particle_pair, reactiontypes, + surface) + self.interaction_type = interaction_type + self.shell_list = [(shell_id, shell), ] + + def determine_next_event(self): + """Return an (event time, event type)-tuple. + + """ + return min(self.draw_escape_time_tuple(), + self.draw_iv_interaction_or_escape_time_tuple(), + self.draw_reaction_time_tuple()) + + def draw_iv_interaction_or_escape_time_tuple(self): + """Return an (interaction/escape time, event type)-tuple. + + Note: we are not calling single.draw_iv_event_type() just yet, but + postpone it to the very last minute (when this event is executed + in fire_single). So IV_EVENT can still be an iv escape or an iv + interaction. + + """ + dt = draw_time_wrapper(self.iv_greens_function()) + + return dt, EventType.IV_EVENT + + def draw_iv_event_type(self): + gf = self.iv_greens_function() + event_kind = draw_event_type_wrapper(gf, self.dt) + if event_kind == PairEventKind.IV_REACTION: + return EventType.IV_REACTION + elif event_kind == PairEventKind.IV_ESCAPE: + return EventType.IV_ESCAPE + raise NotImplemented() + + +class PlanarSurfaceInteraction(InteractionSingle): + pass + + +class CylindricalSurfaceInteraction(InteractionSingle): + """1 Particle close to a CylindricalSurface, inside a cylindrical shell + that surrounds the surface. + + * Particle coordinates inside shell: r, theta, z. + * Domains: composite r-theta, cartesian z. + * Initial position: r = r, theta = 0, z = z. + * Selected randomly when drawing displacement vector: none. + + """ + def __init__(self, domain_id, pid_particle_pair, shell_id, reactiontypes, + interaction_type, surface, projected_point, particle_distance, + orientation_vector, dr, dz_left, dz_right): + + self.particle_distance = particle_distance + self.dz_left = dz_left + + # Only needed for this type of Interaction. + self.unit_r = normalize(pid_particle_pair[1].position - projected_point) + + shell = self.create_shell(domain_id, projected_point, + particle_distance, orientation_vector, + dr, dz_left, dz_right) + + InteractionSingle.__init__(self, domain_id, pid_particle_pair, + shell_id, shell, reactiontypes, + interaction_type, surface) + + def create_shell(self, domain_id, projected_point, particle_distance, + orientation_vector, dr, dz_left, dz_right): + + # Compute origin, radius and half_length of cylinder. + # This stuff is complicated. + half_length = (dz_left + dz_right) / 2 + shiftZ = half_length - dz_left + origin = projected_point + shiftZ * orientation_vector + radius = dr + particle_distance + + # Return shell. + return CylindricalShell(domain_id, Cylinder(origin, radius, + orientation_vector, + half_length)) + + def greens_function(self): + z0 = self.dz_left - self.shell.shape.half_length + + # Free diffusion in z direction. + return GreensFunction1DAbsAbs(self.getD(), self.getv(), z0, + -self.get_mobility_radius(), + self.get_mobility_radius()) + + def iv_greens_function(self): + # Interaction possible in r direction. + # Todo. + #k = self.interaction_type.k + k = 0 + r0 = self.particle_distance + sigma = self.surface.shape.radius + self.pid_particle_pair[1].radius + a_r = self.shell.shape.radius - self.pid_particle_pair[1].radius + # Todo 2D. + return GreensFunction3DRadAbs(self.getD(), k, r0, sigma, a_r) + + def draw_new_position(self, dt, event_type): + # 1) Draw z. + if event_type == EventType.SINGLE_ESCAPE: + # Moving this checks to the Green's functions is not a good + # idea, because then you'd draw an unused random number. + # The same yields for the draw_new_com and draw_new_iv. + z = self.get_mobility_radius() + else: + gf = self.greens_function() + z = draw_r_wrapper(gf, dt, self.get_mobility_radius()) + + # Orientation matters, so use shell.shape.unit_z instead of + # surface.shape.unit_z. + z_vector = z * self.shell.shape.unit_z + + # 2) Draw r and theta. + sigma = self.surface.shape.radius + self.pid_particle_pair[1].radius + a_r = self.shell.shape.radius - self.pid_particle_pair[1].radius + iv_gf = self.iv_greens_function() + if event_type == EventType.IV_ESCAPE: + r = a_r + elif event_type == EventType.IV_INTERACTION: + r = sigma + else: + r = draw_r_wrapper(iv_gf, dt, a_r, sigma) + theta = draw_theta_wrapper(iv_gf, r, dt) + + r_vector = r * rotate_vector(self.unit_r, self.shell.shape.unit_z, + theta) + + # Add displacement to shape.position, not to particle.position. + return self.shell.shape.position + z_vector + r_vector + + def get_shell_size(self): + # Heads up. The cylinder's *half_length*, not radius, + # determines the size in case of a cylindrical surface. + return self.shell_list[0][1].shape.half_length + + def __str__(self): + return 'CylindricalSurfaceInteraction' + Single.__str__(self) + From 4a62a19eb4f41fc53a9e9df68bc9e2e75966b037 Mon Sep 17 00:00:00 2001 From: thomie Date: Wed, 15 Dec 2010 17:49:10 +0100 Subject: [PATCH 0007/1541] Fix check_obj in case of cylinders and spheres --- egfrd.py | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/egfrd.py b/egfrd.py index abec1869..a4b9a8bc 100644 --- a/egfrd.py +++ b/egfrd.py @@ -23,6 +23,7 @@ PlanarSurface, Surface, _random_vector, + Cylinder, Sphere, NetworkRulesWrapper, ) @@ -1891,15 +1892,39 @@ def check_obj(self, obj): if not isinstance(obj, Multi): ignores = [obj.surface.id] else: - ignores = [] + # Ignore all surfaces, multi shells can overlap with + # surfaces. + ignores = [s.id for s in self.world.structures] closest, distance = self.get_closest_obj(shell.shape.position, ignore=[obj.domain_id], ignores=ignores) - if(type(obj) is CylindricalSurfaceSingle or - type(obj) is CylindricalSurfacePair): + if(type(shell.shape) is Cylinder and + closest and type(closest.shell.shape) is Sphere): + # Note: this case is special. + # Note: only checking if cylinder doesn't overlap with + # closest sphere, like we do here, is not really + # sufficient (but checking all spheres is too much + # work). shell_size = shell.shape.half_length + # Reverse overlap calculation: from closest sphere to + # cylinder is easier than the other way around, because + # the distance calculation from a point to a cylinder + # is already implemented. + sphere = closest.shell.shape + diff = self.world.distance(shell.shape, sphere.position) - \ + sphere.radius else: - shell_size = shell.shape.radius + if(type(obj) is CylindricalSurfaceSingle or + type(obj) is CylindricalSurfacePair or + type(obj) is CylindricalSurfaceInteraction): + # On CylindricalSurface, use half_lenghts. + # (assume all nearby other cylinders are on the + # same surface) + shell_size = shell.shape.half_length + else: + # Normally compare radii. + shell_size = shell.shape.radius + diff = distance - shell_size assert shell_size <= self.get_user_max_shell_size(), \ '%s shell size larger than user-set max shell size' % \ @@ -1909,11 +1934,11 @@ def check_obj(self, obj): '%s shell size larger than simulator cell size / 2' % \ str(shell_id) - assert distance - shell_size >= 0.0, \ + assert diff >= 0.0, \ '%s overlaps with %s. (shell: %s, dist: %s, diff: %s.' % \ (str(obj), str(closest), FORMAT_DOUBLE % shell_size, FORMAT_DOUBLE % distance, - FORMAT_DOUBLE % (distance - shell_size)) + FORMAT_DOUBLE % diff) return True From 4e3479fc95efa522936d509072c60b398d5d0ba7 Mon Sep 17 00:00:00 2001 From: thomie Date: Thu, 16 Dec 2010 17:02:37 +0100 Subject: [PATCH 0008/1541] Add burst and propagate InteractionSingles --- egfrd.py | 65 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/egfrd.py b/egfrd.py index a4b9a8bc..8bcd0d2d 100644 --- a/egfrd.py +++ b/egfrd.py @@ -556,6 +556,8 @@ def burst_obj(self, obj): self.remove_event(obj) if __debug__: + # After a burst, InteractionSingles should be gone. + assert all(not isinstance(b, InteractionSingle) for b in bursted) log.info('bursted = %s' % ',\n\t '.join(str(i) for i in bursted)) return bursted @@ -585,7 +587,9 @@ def burst_non_multis(self, neighbors): return bursted - def fire_single_reaction(self, single): + def fire_single_reaction(self, single, interaction_flag=False): + if interaction_flag == True: + raise NotImplementedError reactant_species_radius = single.pid_particle_pair[1].radius oldpos = single.pid_particle_pair[1].position current_surface = single.surface @@ -725,19 +729,34 @@ def propagate_single(self, single): single.pid_particle_pair[0]): raise RuntimeError('propagate_single: check_overlap failed.') - if(single.event_type == EventType.SINGLE_REACTION and - single.event_type != EventType.BURST): - # SINGLE_REACTION, and not a burst. No need to update, single is - # removed anyway. + if(single.event_type == EventType.SINGLE_REACTION or + single.event_type == EventType.IV_INTERACTION): + # Single reaction or interaction, and not a burst. Only + # update particle, single is removed anyway. self.move_single_particle(single, newpos) return single else: - # Todo. if isinstance(single, InteractionSingle): - single.initialize(self.t) - self.move_single(single, newpos, - single.pid_particle_pair[1].radius) - - return single + if isinstance(single, InteractionSingle): + # If for an interaction single a single reaction or iv + # interaction occurs, we create a new single and get + # rid of the old interactionSingle in + # fire_single_reaction. + # For escapes and bursts of interaction singles we do + # it here. + self.move_single_particle(single, newpos) + self.remove_domain(single) + newsingle = self.create_single(single.pid_particle_pair) + if __debug__: + log.debug(' *New %s.\n' + ' radius = %.3g. dt = %.3g.' % + (newsingle, newsingle.shell.shape.radius, + newsingle.dt)) + return newsingle + else: + single.initialize(self.t) + self.move_single(single, newpos, + single.pid_particle_pair[1].radius) + return single def fire_single(self, single): assert abs(single.dt + single.last_time - self.t) <= 1e-18 * self.t @@ -784,6 +803,16 @@ def fire_single(self, single): # Propagate this particle to the exit point on the shell. single = self.propagate_single(single) + # An interaction event is similar to a single reaction. + if single.event_type == EventType.IV_INTERACTION: + try: + self.remove_domain(single) + self.fire_single_reaction(single, interaction_flag=True) + except NoSpace: + self.reject_single_reaction(single) + + return + singlepos = single.shell.shape.position # (2) Clear volume. @@ -897,7 +926,7 @@ def calculate_single_shell_size(self, single, closest, return shell_size def update_single(self, single, closest, distance_to_shell): - # Todo. assert not isinstance(single, InteractionSingle) + assert not isinstance(single, InteractionSingle) singlepos = single.shell.shape.position if isinstance(closest, Single): @@ -1088,8 +1117,16 @@ def burst_single(self, single): old_shell_size - particle_radius # Displacement check is in NonInteractionSingle.draw_new_position. - # Todo. if isinstance(single, InteractionSingle): - self.update_single_event(self.t, single) + if isinstance(single, InteractionSingle): + # Removing the event has to be done for *bursting* + # *Interaction*Singles, not for propagating + # InteractionSingles nor for bursting + # NonInteractionSingles. + self.remove_event(single) + self.add_single_event(newsingle) + else: + assert single == newsingle + self.update_single_event(self.t, single) assert newsingle.shell.shape.radius == particle_radius From bdc955b708cc34e45200295042fdc1df69ce5050 Mon Sep 17 00:00:00 2001 From: thomie Date: Tue, 9 Nov 2010 18:31:04 +0100 Subject: [PATCH 0009/1541] Add CylindricalSurfaceInteractionTestCase --- test/EGFRDSimulator_test.py | 84 +++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/EGFRDSimulator_test.py b/test/EGFRDSimulator_test.py index 59331a90..29790e53 100755 --- a/test/EGFRDSimulator_test.py +++ b/test/EGFRDSimulator_test.py @@ -293,5 +293,89 @@ def test_vtklogger(self): vtk_logger.cleanup() +class CylindricalSurfaceInteractionTestCase(EGFRDSimulatorTestCaseBase): + """Events between the "world" and a cylindrical surface. + + """ + def setUp(self): + self.create_model() + self.add_cylindrical_surface() + + # Only species B is on the cylindrical surface. + self.B["structure"] = "d" + self.add_species() + self.create_simulator() + # Don't add particles yet. + # Don't add all reactions. + + def test_simulation_does_not_come_to_a_halt(self): + # Place a particle on the cylindrical surface. + L = self.L + place_particle(self.s.world, self.B, [L / 2, L / 2, L / 2]) + + # Place a particle very close to the cylindrical surface. + offset = self.radius + place_particle(self.s.world, self.A, + [L / 2, L / 2 + 2 * offset, L / 2 + 2.1 * offset]) + + vtk_logger = vtklogger.VTKLogger(self.s, 'vtk_temp_data') + for i in range(3): + self.s.step() + vtk_logger.log() + vtk_logger.stop() + vtk_logger.cleanup() + + self.failIf(self.s.t < 1.e-10) + + def test_no_pair_between_species_on_different_surfaces(self): + myrandom.seed(0) + # Place a particle on the cylindrical surface. + L = self.L + place_particle(self.s.world, self.B, [L / 2, L / 2, L / 2]) + + # Place a particle very close to the cylindrical surface. + offset = self.radius + place_particle(self.s.world, self.A, + [L / 2, L / 2, L / 2 + 2.1 * offset]) + + vtk_logger = vtklogger.VTKLogger(self.s, 'vtk_temp_data') + + # After 24 steps multi particle tries a move that would result + # in overlap with surface. + self.s.bd_dt_factor = 5 + for i in range(25): + self.s.step() + vtk_logger.log() + self.s.check() + vtk_logger.stop() + vtk_logger.cleanup() + self.failIf(numpy.array(self.s.pair_steps.values()).sum() > 0) + + def test_no_overlap_interaction_and_cylindrical_single(self): + myrandom.seed(2) + # Place a particle on the cylindrical surface. + L = self.L + place_particle(self.s.world, self.B, [L / 2, L / 2, L / 2]) + + # Place a particle very close to the cylindrical surface. + offset = self.radius + place_particle(self.s.world, self.A, + [L / 2, L / 2 + 3 * offset, L / 2 + 2.1 * offset]) + + vtk_logger = vtklogger.VTKLogger(self.s, 'vtk_temp_data') + for i in range(10): + self.s.step() + vtk_logger.log() + self.s.check() + vtk_logger.stop() + vtk_logger.cleanup() + + def test_longer_run(self): + self.add_particles(20) + + for i in range(100): + self.s.step() + + if __name__ == "__main__": unittest.main() From 9ced75224e6c5f1b22d3f171e143142b6e5cb8a8 Mon Sep 17 00:00:00 2001 From: thomie Date: Thu, 16 Dec 2010 17:19:57 +0100 Subject: [PATCH 0010/1541] Add PlanarSurfaceInteractionTestCase --- test/EGFRDSimulator_test.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/EGFRDSimulator_test.py b/test/EGFRDSimulator_test.py index 29790e53..db9c2100 100755 --- a/test/EGFRDSimulator_test.py +++ b/test/EGFRDSimulator_test.py @@ -293,6 +293,30 @@ def test_vtklogger(self): vtk_logger.cleanup() +class PlanarSurfaceInteractionTestCase(EGFRDSimulatorTestCaseBase): + """Events between the "world" and a planar surface. + + """ + def setUp(self): + self.create_model() + self.add_planar_surface() + + # Only species B is on the planar surface. + self.B["structure"] = "m1" + self.add_species() + self.create_simulator() + self.add_particles(10) + # Don't add all reactions. + + def test_interaction_single_is_formed(self): + # Place a particle very close to the planar surface. + z_position = float(self.A['radius']) * (1 + 1e-8) + place_particle(self.s.world, self.A, [0.0,0.0, z_position]) + + for i in range(100): + self.s.step() + + class CylindricalSurfaceInteractionTestCase(EGFRDSimulatorTestCaseBase): """Events between the "world" and a cylindrical surface. From 184d0ec455966d1e717d4fcd790a3e1ee6b6915c Mon Sep 17 00:00:00 2001 From: thomie Date: Thu, 16 Dec 2010 17:44:24 +0100 Subject: [PATCH 0011/1541] Add PlanarSurfaceInteraction --- single.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/single.py b/single.py index cb2325a3..8f9d9d6c 100644 --- a/single.py +++ b/single.py @@ -366,7 +366,103 @@ def draw_iv_event_type(self): class PlanarSurfaceInteraction(InteractionSingle): - pass + """1 Particle close to a PlanarSurface, inside a cylindrical shell + placed on top of the surface. + + * Particle coordinates inside shell: r, theta, z. + * Coordinates: radial r, cartesian z. + * Initial position: r = 0, z = z. + * Selected randomly when drawing displacement vector: theta. + + """ + def __init__(self, domain_id, pid_particle_pair, shell_id, reactiontypes, + surface, interaction_type, origin, orientation, half_length, + particle_offset, projected_point, size_of_domain): + InteractionSingle.__init__(self, domain_id, pid_particle_pair, + shell_id, reactiontypes, surface, + interaction_type, origin, orientation, + half_length, particle_offset, + projected_point, size_of_domain) + + # Compute from dr, dz_left and dz_right the length of the new + # domain (only that part of the cylinder where the particle can + # diffuse to) and the radius and half_length of the new + # cylinder. + + # a + # sigma + sigma = self.surface.shape.Lz + self.pid_particle_pair[1].radius + # r0 + if isinstance(surface, PlanarSurface): + # Heads up: length of domain (the part of the cylinder + # where the particle can get) is a bit different from the + # length of the cylinder because of dz_left. + # Assume surface.Lz == 0 + length_of_domain = particle_distance + dz_right + radius = dr + half_length = (particle_distance + dz_left + dz_right) / 2 + + # TODO. This is not correct anymore. + if isinstance(surface, PlanarSurface): + # Compute new particle offset relative to origin of the + # domain in the z-direction (z=0). + # The surface boundary is at z=-length_of_domain / 2, the + # other end in the middles*at* the surface boundary. (z=L) + # is at the end of the cylinder. + # (min_radius correction in the constructor). + particle_offset = [0, particle_distance - length_of_domain.Lz] + + def greens_function_r(self): + # Todo. 2D gf Abs Sym. + #gf = FirstPassageGreensFunction2D(self.getD()) + return FirstPassageGreensFunction(self.getD(), + self.get_mobility_radius()) + + def greens_function_z(self): + # Todo. 1D gf Rad Abs should be sigma to a. + #gf = FirstPassageGreensFunction1DRad(self.D_tot, self.rt.k) + + return FirstPassagePairGreensFunction(self.getD(), + self.interaction_type.k, self.r0, + self.sigma, self.sigma) + gf.seta(a) + return gf + + # TODO. + + # Cartesian domain of half_length L. Correction with getMinRadius(). + #zDomain = CartesianDomain(particle_offset[1] - self.getMinRadius(), + # size_of_domain - 2 * self.getMinRadius(), + # gfz) + + def createNewShell(self, position, radius, domain_id): + orientation = self.orientation + half_length = self.half_length + return CylindricalShell(position, radius, orientation, half_length, + domain_id) + + def draw_new_positions(self, dt, eventType): + gf_r = self.greens_function_r() + a = self.get_mobility_radius() + r = draw_displacement_wrapper(gf_r, dt, eventType, a) + x, y = randomVector2D(r) + + # Todo. Cartesian coordinate will return absolute position. + gf_z = self.greens_function_z() + # Heads up. size_of_domain is different from the half_length of + # the cylinder, because the domain ends where the surface + # starts, while the cylinder is continuing into the surface. + a = 1 #TODO + r0 = self.particle_offset + sigma = self.sigma + z = draw_displacement_wrapper(gf_z, dt, eventType, a, r0, sigma) + + # Add displacement vector to self.particle.pos, not self.pos. + return self.particle.pos + x * self.interactionSurface.unitX + \ + y * self.interactionSurface.unitY + z * self.shellList[0].unitZ + + def __str__(self): + return 'PlanarSurfaceInteraction' + Single.__str__(self) class CylindricalSurfaceInteraction(InteractionSingle): From 534adf5698d0566fa02d9b688d51d05ea76f4bec Mon Sep 17 00:00:00 2001 From: thomie Date: Fri, 7 Jan 2011 21:12:20 +0100 Subject: [PATCH 0012/1541] Work in progress --- single.py | 19 +++++++++++++++++++ test/EGFRDSimulator_test.py | 11 +++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/single.py b/single.py index 8f9d9d6c..4c5f282d 100644 --- a/single.py +++ b/single.py @@ -568,3 +568,22 @@ def get_shell_size(self): def __str__(self): return 'CylindricalSurfaceInteraction' + Single.__str__(self) +class DummySingle(object): + def __init__(self): + self.multiplicity = 1 + self.half_length = 0.0 + self.shellList = [Sphere(NOWHERE, 0.0)] + + def getMinSize(self): + return 0.0 + + def getD(self): + return 0.0 + + def getPos(self): + return NOWHERE + pos = property(getPos) + + def __str__(self): + return 'DummySingle()' + diff --git a/test/EGFRDSimulator_test.py b/test/EGFRDSimulator_test.py index db9c2100..fc71a73f 100755 --- a/test/EGFRDSimulator_test.py +++ b/test/EGFRDSimulator_test.py @@ -15,7 +15,8 @@ import gfrdbase import myrandom -log.setLevel(logging.WARNING) +#log.setLevel(logging.WARNING) +log.setLevel(logging.DEBUG) class EGFRDSimulatorTestCase(unittest.TestCase): @@ -402,4 +403,10 @@ def test_longer_run(self): if __name__ == "__main__": - unittest.main() + suite = unittest.TestLoader().loadTestsFromTestCase(CylindricalSurfaceInteractionTestCase) + unittest.TextTestRunner(verbosity=2).run(suite) + + # Todo: taking huge bd steps. + + #unittest.main() + From 938ee5772278f9cd0bbcdc5e5ed6b67691463dbb Mon Sep 17 00:00:00 2001 From: Thomas Miedema Date: Fri, 29 Apr 2011 23:16:58 +0200 Subject: [PATCH 0013/1541] Visualize extra particle for arbitrary timesteps Tricking Paraview into showing 2 steps for each simulator step. --- visualization/vtklogger.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/visualization/vtklogger.py b/visualization/vtklogger.py index 0a1ee26a..41743d52 100644 --- a/visualization/vtklogger.py +++ b/visualization/vtklogger.py @@ -85,9 +85,8 @@ def __init__(self, sim, dir='vtkdata', buffer_size=None, show_shells=True, # Step counter. self.i = 0 - # Needed for ParaView time hack. Note: don't make delta_t too - # small, should be relative to max time. - self.delta_t = 1e-11 + # Needed for ParaView time hack. Note: don't make delta_t too small. + self.delta_t = 1e-10 self.last_time = INF def cleanup(self): @@ -118,7 +117,7 @@ def log(self): # # Now ParaView should perceive a state change. # Only needed when showing shells (this is a choice). - time = self.last_time + self.delta_t + time = self.last_time * (1 + self.delta_t) + self.delta_t # Get data. particle_data = self.get_particle_data() @@ -169,7 +168,7 @@ def writelog(self, time, index, self.make_snapshot('cylinder_data', cylinder_data_1, index, time) # Continue with case. - time += self.delta_t / 2 + time *= (1 + self.delta_t / 2) index += 1 if index == 0: From 5b6e777972b0ef3d09d170184277f4b77501d7fe Mon Sep 17 00:00:00 2001 From: Laurens Bossen Date: Tue, 3 May 2011 16:06:33 +0200 Subject: [PATCH 0014/1541] Added documentation files to track (latex of GFs and pseudocode) --- doc/greens_functions/Makefile | 7 + doc/greens_functions/tex/Makefile | 31 ++ .../tex/greensFunction1DAbsAbs.tex | 13 + .../tex/greensFunction1DAbsInf.tex | 13 + .../tex/greensFunction1DRadAbs.tex | 13 + .../tex/greensFunction1DRadInf.tex | 13 + .../tex/greensFunction2DRadAbs.tex | 12 + doc/greens_functions/tex/greensFunctions.tex | 32 +++ .../tex/greensFunctionsWDrift.tex | 24 ++ .../greensFunction1DAbsAbs.include.tex | 29 ++ .../greensFunction1DAbsInf.include.tex | 27 ++ .../greensFunction1DRadAbs.include.tex | 47 ++++ .../greensFunction1DRadInf.include.tex | 29 ++ .../greensFunction1DwDriftAbsAbs.include.tex | 47 ++++ .../greensFunction1DwDriftAbsInf.include.tex | 35 +++ .../greensFunction1DwDriftRadAbs.include.tex | 67 +++++ .../greensFunction1DwDriftRadInf.include.tex | 40 +++ .../greensFunction2DRadAbs.include.tex | 62 ++++ doc/greens_functions/tex/include/include.tex | 27 ++ .../tex/include/subSectionOnly.tex | 27 ++ doc/pseudocode/Makefile | 15 + doc/pseudocode/diagrams.html | 9 + doc/pseudocode/diagrams/Makefile | 22 ++ doc/pseudocode/diagrams/createMulti.dot | 23 ++ doc/pseudocode/diagrams/egfrd.dot | 29 ++ doc/pseudocode/diagrams/fireMulti.dot | 15 + doc/pseudocode/diagrams/firePair.dot | 42 +++ doc/pseudocode/diagrams/fireSingle.dot | 38 +++ doc/pseudocode/pseudocode.py | 265 ++++++++++++++++++ doc/visualization.txt | 158 +++++++++++ 30 files changed, 1211 insertions(+) create mode 100644 doc/greens_functions/Makefile create mode 100644 doc/greens_functions/tex/Makefile create mode 100644 doc/greens_functions/tex/greensFunction1DAbsAbs.tex create mode 100644 doc/greens_functions/tex/greensFunction1DAbsInf.tex create mode 100644 doc/greens_functions/tex/greensFunction1DRadAbs.tex create mode 100644 doc/greens_functions/tex/greensFunction1DRadInf.tex create mode 100644 doc/greens_functions/tex/greensFunction2DRadAbs.tex create mode 100644 doc/greens_functions/tex/greensFunctions.tex create mode 100644 doc/greens_functions/tex/greensFunctionsWDrift.tex create mode 100644 doc/greens_functions/tex/include/greensFunction1DAbsAbs.include.tex create mode 100644 doc/greens_functions/tex/include/greensFunction1DAbsInf.include.tex create mode 100644 doc/greens_functions/tex/include/greensFunction1DRadAbs.include.tex create mode 100644 doc/greens_functions/tex/include/greensFunction1DRadInf.include.tex create mode 100644 doc/greens_functions/tex/include/greensFunction1DwDriftAbsAbs.include.tex create mode 100644 doc/greens_functions/tex/include/greensFunction1DwDriftAbsInf.include.tex create mode 100644 doc/greens_functions/tex/include/greensFunction1DwDriftRadAbs.include.tex create mode 100644 doc/greens_functions/tex/include/greensFunction1DwDriftRadInf.include.tex create mode 100644 doc/greens_functions/tex/include/greensFunction2DRadAbs.include.tex create mode 100644 doc/greens_functions/tex/include/include.tex create mode 100644 doc/greens_functions/tex/include/subSectionOnly.tex create mode 100644 doc/pseudocode/Makefile create mode 100644 doc/pseudocode/diagrams.html create mode 100644 doc/pseudocode/diagrams/Makefile create mode 100644 doc/pseudocode/diagrams/createMulti.dot create mode 100644 doc/pseudocode/diagrams/egfrd.dot create mode 100644 doc/pseudocode/diagrams/fireMulti.dot create mode 100644 doc/pseudocode/diagrams/firePair.dot create mode 100644 doc/pseudocode/diagrams/fireSingle.dot create mode 100644 doc/pseudocode/pseudocode.py create mode 100644 doc/visualization.txt diff --git a/doc/greens_functions/Makefile b/doc/greens_functions/Makefile new file mode 100644 index 00000000..5670cce2 --- /dev/null +++ b/doc/greens_functions/Makefile @@ -0,0 +1,7 @@ + +.PHONY: all +all: + cd tex && $(MAKE) $(MKFLAGS) && cd ../ && mv -f build/*.pdf . + +clean: + rm -rf build/*.aux build/*.log build/include/*.aux build/include/*.log *.pdf diff --git a/doc/greens_functions/tex/Makefile b/doc/greens_functions/tex/Makefile new file mode 100644 index 00000000..3b842c79 --- /dev/null +++ b/doc/greens_functions/tex/Makefile @@ -0,0 +1,31 @@ +# Todo. Use that Makefile.am magic. And why is it recompiling even though +# nothing changed? + +# make should produce: +# * 1 pdf for the derivation of the Greens function of each diffusion +# problem. (Todo) +# * 1 pdf for each problem giving the results: Greens function, survival +# probability and propensity function. +# * greensFunctions.pdf, all results combined. + + +PDFLATEX=pdflatex -output-directory=../build + +greensFunctions.pdf: greensFunctions.tex greensFunction1DAbsAbs.pdf greensFunction1DAbsInf.pdf greensFunction1DRadAbs.pdf greensFunction1DRadInf.pdf greensFunction2DRadAbs.pdf + $(PDFLATEX) greensFunctions.tex + +greensFunction1DAbsAbs.pdf: greensFunction1DAbsAbs.tex include/greensFunction1DAbsAbs.include.tex + $(PDFLATEX) greensFunction1DAbsAbs.tex + +greensFunction1DAbsInf.pdf: greensFunction1DAbsInf.tex include/greensFunction1DAbsInf.include.tex + $(PDFLATEX) greensFunction1DAbsInf.tex + +greensFunction1DRadAbs.pdf: greensFunction1DRadAbs.tex include/greensFunction1DRadAbs.include.tex + $(PDFLATEX) greensFunction1DRadAbs.tex + +greensFunction1DRadInf.pdf: greensFunction1DRadInf.tex include/greensFunction1DRadInf.include.tex + $(PDFLATEX) greensFunction1DRadInf.tex + +greensFunction2DRadAbs.pdf: greensFunction2DRadAbs.tex include/greensFunction2DRadAbs.include.tex + $(PDFLATEX) greensFunction2DRadAbs.tex + diff --git a/doc/greens_functions/tex/greensFunction1DAbsAbs.tex b/doc/greens_functions/tex/greensFunction1DAbsAbs.tex new file mode 100644 index 00000000..77538747 --- /dev/null +++ b/doc/greens_functions/tex/greensFunction1DAbsAbs.tex @@ -0,0 +1,13 @@ + +\documentclass{article} + +\include{include/include} +\include{include/subSectionOnly} + + +\begin{document} + +\include{include/greensFunction1DAbsAbs.include} +\include{include/greensFunction1DwDriftAbsAbs.include} + +\end{document} diff --git a/doc/greens_functions/tex/greensFunction1DAbsInf.tex b/doc/greens_functions/tex/greensFunction1DAbsInf.tex new file mode 100644 index 00000000..f0a1dfd2 --- /dev/null +++ b/doc/greens_functions/tex/greensFunction1DAbsInf.tex @@ -0,0 +1,13 @@ + +\documentclass{article} + +\include{include/include} +\include{include/subSectionOnly} + + +\begin{document} + +\include{include/greensFunction1DAbsInf.include} +\include{include/greensFunction1DwDriftAbsInf.include} + +\end{document} diff --git a/doc/greens_functions/tex/greensFunction1DRadAbs.tex b/doc/greens_functions/tex/greensFunction1DRadAbs.tex new file mode 100644 index 00000000..83ce9cf2 --- /dev/null +++ b/doc/greens_functions/tex/greensFunction1DRadAbs.tex @@ -0,0 +1,13 @@ + +\documentclass{article} + +\include{include/include} +\include{include/subSectionOnly} + + +\begin{document} + +\include{include/greensFunction1DRadAbs.include} +\include{include/greensFunction1DwDriftRadAbs.include} + +\end{document} diff --git a/doc/greens_functions/tex/greensFunction1DRadInf.tex b/doc/greens_functions/tex/greensFunction1DRadInf.tex new file mode 100644 index 00000000..a2adf5e3 --- /dev/null +++ b/doc/greens_functions/tex/greensFunction1DRadInf.tex @@ -0,0 +1,13 @@ + +\documentclass{article} + +\include{include/include} +\include{include/subSectionOnly} + + +\begin{document} + +\include{include/greensFunction1DRadInf.include} +\include{include/greensFunction1DwDriftRadInf.include} + +\end{document} diff --git a/doc/greens_functions/tex/greensFunction2DRadAbs.tex b/doc/greens_functions/tex/greensFunction2DRadAbs.tex new file mode 100644 index 00000000..f6b04146 --- /dev/null +++ b/doc/greens_functions/tex/greensFunction2DRadAbs.tex @@ -0,0 +1,12 @@ + +\documentclass{article} + +\include{include/include} +\include{include/subSectionOnly} + + +\begin{document} + +\include{include/greensFunction2DRadAbs.include} + +\end{document} diff --git a/doc/greens_functions/tex/greensFunctions.tex b/doc/greens_functions/tex/greensFunctions.tex new file mode 100644 index 00000000..3dbaf570 --- /dev/null +++ b/doc/greens_functions/tex/greensFunctions.tex @@ -0,0 +1,32 @@ + +\documentclass[twoside]{report} + +\include{include/include} +\include{include/subSectionOnly} +%Page numbers instead of name. +\fancyfoot[RO, LE]{\thepage} + + +\begin{document} + +\title{Greens functions, survival probabilies and propensity functions (1D and +2D)} +\thispagestyle{empty} +\maketitle + + +\newpage\input{include/greensFunction1DAbsAbs.include} +\newpage\input{include/greensFunction1DwDriftAbsAbs.include} + +\newpage\include{include/greensFunction1DAbsInf.include} +\newpage\include{include/greensFunction1DwDriftAbsInf.include} + +\newpage\include{include/greensFunction1DRadAbs.include} +\newpage\include{include/greensFunction1DwDriftRadAbs.include} + +\newpage\include{include/greensFunction1DRadInf.include} +\newpage\include{include/greensFunction1DwDriftRadInf.include} + +\newpage\include{include/greensFunction2DRadAbs.include} + +\end{document} diff --git a/doc/greens_functions/tex/greensFunctionsWDrift.tex b/doc/greens_functions/tex/greensFunctionsWDrift.tex new file mode 100644 index 00000000..efc4649d --- /dev/null +++ b/doc/greens_functions/tex/greensFunctionsWDrift.tex @@ -0,0 +1,24 @@ + +\documentclass[twoside]{report} + +\include{include/include} +\include{include/subSectionOnly} +%Page numbers instead of name. +\fancyfoot[RO, LE]{\thepage} + + +\begin{document} + +\title{Greens functions, survival probabilies and propensity functions (1D and +2D)} +\thispagestyle{empty} +\maketitle + + +\input{include/greensFunction1DwDriftAbsAbs.include} +\include{include/greensFunction1DwDriftAbsInf.include} +\include{include/greensFunction1DwDriftRadAbs.include} +\include{include/greensFunction1DwDriftRadInf.include} +\include{include/greensFunction2DwDriftRadAbs.include} + +\end{document} diff --git a/doc/greens_functions/tex/include/greensFunction1DAbsAbs.include.tex b/doc/greens_functions/tex/include/greensFunction1DAbsAbs.include.tex new file mode 100644 index 00000000..e5118ced --- /dev/null +++ b/doc/greens_functions/tex/include/greensFunction1DAbsAbs.include.tex @@ -0,0 +1,29 @@ + +\subsection{1D Abs Abs} + +\paragraph{Green's function} +\begin{align} + p(z,t|z',t'=0) = \frac{2}{L}\sum_{n=1}^{\infty} + e^{-D_1n^2\pi^2t/L^2} \sin\bigg(\frac{n\pi z}{L}\bigg) \sin\left(\frac{n\pi z'}{L}\right). +\end{align} + +\paragraph{Survival probability} +\begin{align} + S_z(t) = \int_0^L p dz = \frac{4}{\pi} \sum_{m=0}^{\infty} \frac{1}{2m+1} + e^{-D_1(2m+1)^2\pi^2t/L^2} \sin\left(\frac{(2m+1)\pi z'}{L}\right). +\end{align} + +\paragraph{Propensity function} +\begin{align} + q_{z=0}(t) &= \left.D_1 \pd{p}{z}\right|_{z=0} = \frac{2\pi D_1}{L^2} + \sum_{n=1}^{\infty} e^{-D_1n^2\pi^2t/L^2} n \sin\left(\frac{n\pi z'}{L}\right),\\ + q_{z=L}(t) &= \left.- D_1 \pd{p}{z}\right|_{z=L} = \frac{2\pi D_1}{L^2} + \sum_{n=1}^{\infty} e^{-D_1n^2\pi^2t/L^2} n (-1)^{n+1} \sin\left(\frac{n\pi z'}{L}\right). +\end{align} + +\paragraph{Cumulative Green's function} +\begin{align} + P(Z,t|z',t'=0) = \int_0^Z p dz = + \frac{2}{\pi} \sum_{n=1}^{\infty} e^{-D_1n^2\pi^2 t/L^2} + \frac{1}{n} \left[ 1 - \cos\left(\frac{n\pi Z}{L}\right) \right] \sin\left(\frac{n\pi z'}{L}\right). +\end{align} \ No newline at end of file diff --git a/doc/greens_functions/tex/include/greensFunction1DAbsInf.include.tex b/doc/greens_functions/tex/include/greensFunction1DAbsInf.include.tex new file mode 100644 index 00000000..2329a085 --- /dev/null +++ b/doc/greens_functions/tex/include/greensFunction1DAbsInf.include.tex @@ -0,0 +1,27 @@ + +\subsection{1D Abs Inf} + +\paragraph{Green's function} +\begin{align} + p(z,t|z',t'=0) &= \frac{1}{\sqrt{4\pi D_1t}} \left[ e^{-\frac{1}{4D_1t}(z-z')^2} - e^{-\frac{1}{4D_1t}(z+z')^2} \right]. \\ + &= \frac{1}{\sqrt{4\pi D_1t}} \left[ 1 - e^{-\frac{zz'}{D_1t}} \right]e^{-\frac{1}{4D_1t}(z-z')^2}. +\end{align} + +\paragraph{Survival probability} +\begin{align} + S_{z=0}(t) = \int_0^\infty p dz = + \text{erf}\left( \frac{z'}{\sqrt{4D_1 t}} \right). +\end{align} + +\paragraph{Propensity functions} +\begin{align} + q_{z=0}(t) = -\frac{d}{dt} S_{z=0}(t)= + \frac{z'}{2\sqrt{\pi D_1 t^3}} e^{-\frac{z'^2}{4D_1t}}. +\end{align} + +\paragraph{Cumulative Green's function} +\begin{align} + P_{z=0}(Z,t) = \int_a^Z p dz = + \erf{\left(\frac{z'}{\sqrt{4D_1t}}\right)} + + \frac{1}{2} \left[ \erf{\left(\frac{Z-z'}{\sqrt{4D_1t}}\right)} - \erf{\left(\frac{Z+z'}{\sqrt{4D_1t}}\right)} \right] +\end{align} \ No newline at end of file diff --git a/doc/greens_functions/tex/include/greensFunction1DRadAbs.include.tex b/doc/greens_functions/tex/include/greensFunction1DRadAbs.include.tex new file mode 100644 index 00000000..5d92fab2 --- /dev/null +++ b/doc/greens_functions/tex/include/greensFunction1DRadAbs.include.tex @@ -0,0 +1,47 @@ + +\subsection{1D Rad Abs} + +\renewcommand{\an}{\rho_n} + +\paragraph{Green's function} +\begin{align} + p(z,t|z',t'=0) &= 2\sum_{n=0}^{\infty} + e^{-D_1\an^2t} \frac + { F_n(z')F_n(z) } + {h + (\an^2 + h^2)L}, \\ + h &= k_+ / D_1, \\ + F_n(z) &= h\sin\an z + \an\cos\an z , \\ + \tan\an L &= -\frac{\an}{h}. +\end{align} + +\paragraph{Survival probability} +\begin{align} + S_z(t) = \int_0^L pdz + = 2\sum_{n=0}^{\infty} e^{-D_1\an^2t} + \frac{F_n(z')}{h + (\an^2 + h^2)L} + \frac{\left[h^2 - (\an^2 + h^2) \cos(\an L)\right]}{h\an}. +\end{align} + +\paragraph{Propensity function} +\begin{align} + q_{z=0}(t) = k_+ p|_{z=0} = + 2hD_1 \sum_{n=0}^{\infty} e^{-D_1\an^2t} + \frac{\an F_n(z')}{h + (\an^2 + h^2)L}, +\end{align} +\begin{align} + q_{z=L}(t) &= \left.- D_1 \pd{p}{z}\right|_{z=L} \\ + &= -2D_1 \sum_{n=0}^{\infty} e^{-D_1\an^2t} + \frac{ + F_n(z') \left[\frac{1}{h}\an^3 + h\an\right] \cos(\an L) + } + {h + (\an^2 + h^2)L}. +\end{align} + +\paragraph{Cumulative Green's function} +\begin{align} + P(Z,t|z',t'=0)) &= \int_0^Z pdz \\ + &= 2\sum_{n=0}^{\infty} e^{-D_1\an^2t} + \frac{F_n(z')}{h + (\an^2 + h^2)L} + \frac{\left[h^2 + h\an\sin(\an Z) - h^2 \cos(\an Z)\right]}{h\an}. +\end{align} + diff --git a/doc/greens_functions/tex/include/greensFunction1DRadInf.include.tex b/doc/greens_functions/tex/include/greensFunction1DRadInf.include.tex new file mode 100644 index 00000000..5a7d78c1 --- /dev/null +++ b/doc/greens_functions/tex/include/greensFunction1DRadInf.include.tex @@ -0,0 +1,29 @@ + +\subsection{1D Rad Inf} + +\paragraph{Green's function} +\begin{multline} + v(z,t|z',t'=0) = \frac{1}{\sqrt{4\pi D_1 t}} [ + e^{-(z-z')^2/(4 D_1 t)} + + e^{-(z+z')^2/(4 D_1 t)} ] \\ + -h e^{h(z+z') + D_1 th^2} \erfc(\frac{z+z'}{2\sqrt{D_1t}} + h\sqrt{D_1t}) + .\label{green-promoter-solo} +\end{multline} + +\paragraph{Survival probability} +\begin{align} + S_{z=0}(t) = 1 + e^{h z' + D_1 t h^2} + \erfc(\frac{z'}{2 \sqrt{D_1 t}} + h \sqrt{D_1 t}) - + \erfc(\frac{z'}{2 \sqrt{D_1 t}}). +\end{align} + +\paragraph{Propensity function} +% Not in my notes (Thomas). +\begin{align} + q_{z=0}(t) &= k_+ v|_{z=0} \\ + &= \frac{h \sqrt{D_1}}{\sqrt{\pi t}} e^{-z'^2/(4 D_1 t)} + -h^2D_1e^{hz'+D_1th^2} \erfc(\frac{z'}{2\sqrt{D_1t}} + kh\sqrt{D_1t}). +\end{align} +\begin{multline} +\end{multline} + diff --git a/doc/greens_functions/tex/include/greensFunction1DwDriftAbsAbs.include.tex b/doc/greens_functions/tex/include/greensFunction1DwDriftAbsAbs.include.tex new file mode 100644 index 00000000..28412252 --- /dev/null +++ b/doc/greens_functions/tex/include/greensFunction1DwDriftAbsAbs.include.tex @@ -0,0 +1,47 @@ + +\subsection{1D Abs Abs with drift $v\neq 0$} + +Solution on an interval $[a,b]$, initial condition $p(z,z',t'=0)=\delta (z-z')$. + +\paragraph{Green's function} +\begin{align} + p(z,t|z',t'=0) &= \frac{2}{b-a}e^{-\frac{1}{4D_1t}\left[v^2t^2 - 2(z-z')vt \right]} + \sum_{n=1}^{\infty} + e^{-D_1\left(\frac{n\pi}{b-a}\right)^2 t} \sin\left(\frac{z-a}{b-a}n\pi\right) \sin\left(\frac{z'-a}{b-a}n\pi\right). +\end{align} + +\paragraph{Survival probability} +\begin{align} + S_z(t) = \int_a^b pdz + = 2e^{-\frac{1}{4D_1t}\left[ v^2t^2 + 2 z'vt \right]} + \sum_{n=1}^{\infty} + \frac{ e^{-D_1\left(\frac{n\pi}{b-a}\right)^2 t} \left[ e^{\frac{va}{2D_1}} - (-1)^n e^{\frac{vb}{2D_1}}\right] } + { \frac{v^2}{4D_1^2}(b-a)^2 + n^2\pi^2 } + n\pi \sin\left(\frac{z'-a}{b-a}n\pi \right). +\end{align} + +\paragraph{Propensity function} +\begin{align} + q_{z=0}(t) &= \left.D_1 \pd{p}{z}\right|_{z=a} = \frac{2 D_1}{(b-a)^2} e^{-\frac{1}{4D_1 t}\left[ v^2t^2 - 2(a-z')vt \right]} + \sum_{n=1}^{\infty} e^{-D_1\left(\frac{n\pi}{b-a}\right)^2 t} n\pi \sin\left(\frac{z'-a}{b-a}n\pi\right),\\ + q_{z=L}(t) &= \left.- D_1 \pd{p}{z}\right|_{z=b} = \frac{2 D_1}{(b-a)^2} e^{-\frac{1}{4D_1 t}\left[ v^2t^2 - 2(b-z')vt \right]} + \sum_{n=1}^{\infty} (-1)^{n+1} e^{-D_1\left(\frac{n\pi}{b-a}\right)^2 t} n\pi \sin\left(\frac{z'-a}{b-a}n\pi\right). +\end{align} + +\paragraph{Cumulative Green's function} +\begin{align} + & P(Z,t|z',t'=0) = \int_a^Z p dz &\\ + & = 2e^{-\frac{1}{4D_1t}\left[v^2t^2 + 2z'vt \right]} &\\ + & \quad\times \sum_{n=1}^{\infty} + \frac{ e^{-D_1\left(\frac{n\pi}{b-a}\right)^2 t} + \left[ + e^{\frac{va}{2D_1}} + e^{\frac{vZ}{2D_1}} + \left\lbrace + \frac{v}{2D_1}\frac{(b-a)}{n\pi}\sin\left(\frac{Z-a}{b-a}n\pi\right) + - \cos\left(\frac{Z-a}{b-a}n\pi\right) + \right\rbrace + \right] + } + { \frac{v^2}{4D_1^2}(b-a)^2 + n^2\pi^2 } + n\pi \sin\left(\frac{z'-a}{b-a}n\pi\right).& +\end{align} \ No newline at end of file diff --git a/doc/greens_functions/tex/include/greensFunction1DwDriftAbsInf.include.tex b/doc/greens_functions/tex/include/greensFunction1DwDriftAbsInf.include.tex new file mode 100644 index 00000000..39ec09da --- /dev/null +++ b/doc/greens_functions/tex/include/greensFunction1DwDriftAbsInf.include.tex @@ -0,0 +1,35 @@ + +\subsection{1D Abs Inf with drift $v\neq 0$} + +Absorbing boundary at $z=0$. + +\paragraph{Green's function} +\begin{align} + p(z,t|z',t'=0) &= e^{-\frac{1}{4D_1t}\left[v^2t^2 - 2(z-z')vt \right]} \frac{1}{\sqrt{4\pi D_1t}} + \left[ e^{-\frac{1}{4D_1t}(z-z')^2} - e^{-\frac{1}{4D_1t}(z+z')^2} \right]. \\ + &= \frac{1}{\sqrt{4\pi D_1t}} \left[ 1 - e^{-\frac{zz'}{D_1t}} \right]e^{-\frac{1}{4D_1t}(z-z'-vt)^2}. +\end{align} + +\paragraph{Survival probability} +\begin{align} + S_{z=0}(t) &= \int_a^\infty p dz \\ +& = \frac{1}{2}\left( 1 - e^{-\frac{vz'}{D_1}} \right) + + \frac{1}{2} \left[ \erf{\left(\frac{z'+vt}{\sqrt{4D_1t}}\right)} + e^{-\frac{vz'}{D_1}} \erf{\left(\frac{z'-vt}{\sqrt{4D_1t}}\right)} \right] . +\end{align} + +\paragraph{Propensity functions} +\begin{align} + q_{z=0}(t) = -\frac{d}{dt} S_{z=0}(t)= + \frac{z'}{\sqrt{4 \pi D_1 t^3}} e^{-\frac{1}{4D_1t}(z'+vt)^2} . +\end{align} + +\paragraph{Cumulative Green's function} +\begin{align} + P_{z=0}(Z,t) &= \int_a^Z p dz \\ +& = \frac{1}{2} \left[ \erf{\left(\frac{z'+vt}{\sqrt{4D_1t}}\right)} + \erf{\left(\frac{Z-z'-vt}{\sqrt{4D_1t}}\right)} \right] + +\frac{1}{2} e^{-\frac{vz'}{D_1}} \left[ \erf{\left(\frac{z'-vt}{\sqrt{4D_1t}}\right)} - \erf{\left(\frac{Z+z'-vt}{\sqrt{4D_1t}}\right)} \right] . +\end{align} + +\vspace{1cm} +\noindent \textit{For the case 1D Refl Inf with drift see:}\\ +\textit{\textsc{Smoluchowski}, Phys.Z. 17, p. 557-587, 1916, eq. (52)--(54)}. \ No newline at end of file diff --git a/doc/greens_functions/tex/include/greensFunction1DwDriftRadAbs.include.tex b/doc/greens_functions/tex/include/greensFunction1DwDriftRadAbs.include.tex new file mode 100644 index 00000000..13b2d608 --- /dev/null +++ b/doc/greens_functions/tex/include/greensFunction1DwDriftRadAbs.include.tex @@ -0,0 +1,67 @@ + +\subsection{1D Rad Abs with drift $v\neq 0$} + +Solution on an interval $[a,b]$, initial condition $p(z,z',t'=0)=\delta (z-z')$. + +\newcommand{\rn}{\rho_n} + +\paragraph{Green's function} +\begin{align} + & p(z,t|z',t'=0) = 2e^{-\frac{1}{4D_1t}\left[v^2t^2 - 2(z-z')vt \right]} + \sum_{n=0}^{\infty} e^{-D_1\rn^2t} \frac{ F_n(z')F_n(z) } + {h + \left[\rn^2 + h^2\right]L}, \\ + &\quad\quad F_n(z) = h\sin\left(\rn(z-a)\right) + \rn\cos\left(\rn(z-a)\right) , \\ + &\quad\quad h = \frac{v/2 + k_+}{D_1}, \quad\quad L=(b-a), \quad\quad \tan\left(\rn L\right)=-\frac{\rn}{h}. +\end{align} + +\paragraph{Survival probability} +\begin{align} + S_z(t) + &= \int_a^b pdz = 2e^{-\frac{1}{4D_1t}\left[ v^2t^2 + 2z'vt \right]} \\ + &\quad\quad + \times\sum_{n=0}^{\infty} e^{-D_1\rn^2t} + \frac{F_n(z')}{\Big. h + \left[\rn^2 + h^2\right]L \Big.} + \frac{ + \left[ + e^{\frac{va}{2D_1}} h \frac{k_+}{D_1} + - e^{\frac{vb}{2D_1}} \left(\rn^2 + h^2\right) \cos(\rn L) + \right] + } + {\frac{h}{\rn}\left[ \rn^2 + \frac{v^2}{4 D_1^2} \right]}. +\end{align} + +\paragraph{Propensity function} +\begin{align} + q_{z=0}(t) &= k_+ p|_{z=a} = 2k_+ e^{-\frac{1}{4D_1t}\left[v^2t^2 - 2(a-z')vt \right]} + \sum_{n=0}^{\infty} e^{-D_1\rn^2t} + \frac{ \rn F_n(z')} {h + \left[\rn^2 + h^2\right]L}, \\ + q_{z=L}(t) &= \left.- D_1 \pd{p}{z}\right|_{z=b} \\ + &= -2D_1 e^{-\frac{1}{4D_1t}\left[v^2t^2 - 2(b-z')vt \right]} + \sum_{n=0}^{\infty} e^{-D_1\rn^2t} + \frac{F_n(z') \left[\frac{1}{h}\rn^3 + h\rn\right]\cos\left(\rn L\right) + } + {h + \left[\rn^2 + h^2\right]L}. +\end{align} + +\paragraph{Cumulative Green's function} +\begin{multline} + P(Z,t|z',t'=0) = \int_a^Z pdz \\ + \shoveleft{ + = 2e^{-\frac{1}{4D_1t}\left[ v^2t^2 + 2z'vt \right]} + \sum_{n=0}^{\infty} e^{-D_1\rn^2t} \left\lbrace \frac{F_n(z')}{\Big. h + \left[\rn^2 + h^2\right]L \Big.} \right. + } \\ + \shoveright{ + \quad\quad\quad \left.\times + \frac{ + \left[ + e^{\frac{va}{2D_1}} h\frac{k_+}{D_1} + - e^{\frac{vZ}{2D_1}} + \left\lbrace + h\frac{k_+}{D_1}\cos\left(\rn(Z-a)\right) - \left(h\rn + \frac{h^2}{\rn}\frac{v}{2D_1}\right)\sin\left(\rn(Z-a)\right) + \right\rbrace + \right] + } + {\frac{h}{\rn}\left[ \rn^2 + \frac{v^2}{4 D_1^2} \right]} + \right\rbrace . + } +\end{multline} diff --git a/doc/greens_functions/tex/include/greensFunction1DwDriftRadInf.include.tex b/doc/greens_functions/tex/include/greensFunction1DwDriftRadInf.include.tex new file mode 100644 index 00000000..b5530cb7 --- /dev/null +++ b/doc/greens_functions/tex/include/greensFunction1DwDriftRadInf.include.tex @@ -0,0 +1,40 @@ + +\subsection{1D Rad Inf with drift $v\neq 0$} + +\paragraph{Green's function} +\begin{multline} + p(z,t|z',t'=0) = \frac{1}{\sqrt{4\pi D_1 t}} [ + e^{-(z-z'-vt)^2/(4 D_1 t)} + + e^{-\frac{vz'}{D_1}} e^{-(z+z'-vt)^2/(4 D_1 t)} ] \\ + -(h+\frac{v}{2D_1}) e^{\frac{vz'}{D_1}} + e^{h \left[ (z+z') + (h + v/D_1) D_1 t \right]} \erfc\left(\frac{z+z'}{\sqrt{4D_1t}} + (h+\frac{v}{2D_1})\sqrt{D_1t} \right) + .\label{green-promoter-solo-drift} +\end{multline} +\textit{Source: \textsc{Lamm \& Schulten}, J. Chem. Phys. 78(5), 1983.} + +\paragraph{Survival probability} +%\begin{align} +% S_{z=0}(t) &= 1 + \frac{1}{2}\left[ \erf\left( \frac{z'+vt}{4D_1 t}\right) - \erf\left( \frac{z'-vt}{4D_1 t}\right) \right] \\ +% &\quad + \left(\frac{k_+ + \frac{v}{2}}{k_+ + v}\right) +% \times \left\lgroup +% e^{h \left[ z' + \left(h+\frac{v}{D_1}\right) D_1 t \right]} \erfc\left[ \frac{z'}{\sqrt{4D_1 t}} + \left(h+\frac{v}{2D_1}\right)\sqrt{D_1 t} \right] \right.\\ +% &\left. \quad- \, e^{- \frac{v}{D_1}z'} +% \erfc\left[ \frac{z'}{\sqrt{4D_1 t}} - \frac{v}{2D_1}\sqrt{D_1 t} \right] +% \right\rgroup. +%\end{align} +%%% ABOVE VERSION IS WRONG, HAS TO BE RECALCULATED! + +\paragraph{Propensity function} +% Mathematica result (Tomek) +%\begin{align} +% q_{z=0}(t) &= -\partial_t S(t) \\ +% &= \frac{1}{4\sqrt{\pi D_1 t^3}} +% \Bigg\lbrace +% e^{-\frac{(z'+vt)^2}{4D_1 t}} \left[ z'\left( 1-e^{\frac{vz'}{D_1}} \right) +% - vt \left( 1+e^{\frac{vz'}{D_1}}\right) \right] \\ +% & +% + 2t (2k_+ + v) \left[ 1 - h \sqrt{\frac{\pi t}{D_1}} e^{\frac{\left[z'+(2k_+ + v)t\right]^2}{4D_1 t}} +% \erfc\left(\frac{z' + (2k_+ + v)t}{\sqrt{4 D_1 t}} \right) +% \right] \Bigg\rbrace +%\end{align} +%%% ABOVE VERSION IS WRONG, HAS TO BE RECALCULATED! \ No newline at end of file diff --git a/doc/greens_functions/tex/include/greensFunction2DRadAbs.include.tex b/doc/greens_functions/tex/include/greensFunction2DRadAbs.include.tex new file mode 100644 index 00000000..a90d643c --- /dev/null +++ b/doc/greens_functions/tex/include/greensFunction2DRadAbs.include.tex @@ -0,0 +1,62 @@ +\subsection{2D Rad Abs} + +\paragraph{Green's function} +\begin{multline} + v(r,\t, t) = \frac{\pi}{4} \sum_{m=-\infty}^{\infty} \cos m(\t-\t') \\ + \cdot \sum_{n=1}^{\infty} \frac{\amn^2 J_m^2(b\amn)}{F(\amn)} + C_m(r,\amn)C_m(r',\amn)e^{-D_3\amn^2t}, +\end{multline} + +where +\begin{align} + F(\amn) = \left[hJ_m(a\amn) - \amn J_m'(a\amn)\right]^2 + -[\amn^2+h^2-m^2/a^2]J_m^2(b\amn) +\end{align} +and +\begin{multline} + C_m(r,\amn) = J_m(r\amn)\left[hY_m(a\amn)-\amn Y_m'(a\amn)\right]-\\ + Y_m(r\amn)\left[hJ_m(a\amn)-\amn J_m'(a\amn)\right], +\end{multline} +%How can you get F= and C= to align? + +while $\amn$ is the $n$-th root of +\begin{align} + \left[\a J_m'(a\a)-hJ_m(a\a)\right]Y_m(b\a) - + \left[\a Y_m'(a\a)-hY_m(a\a)\right]J_m(b\a) = 0. +\end{align} + + + +\paragraph{Survival probability} +\begin{align} + S(t) &= \int_0^{2\pi}\int_a^bv(r,\t,t)rd\t dr\notag\\ + &= \frac{\pi^2}{2} \cdot \sum_{n=1}^{\infty} + \frac{\azn^2 J_0^2(b\azn)}{F(\azn)} + C_0(r',\azn)e^{-D_3\azn^2t} \cdot \int_a^bC_0(r,\azn)r dr +\end{align} + +where +\begin{align} + \int_a^bC_0(r,\azn)r dr = &\frac{1}{\azn}\left[bJ_1(b\azn) - + aJ_1(a\azn)\right] \left[hY_m(a\azn)-\azn Y_m'(a\azn)\right]-\\ + &\frac{1}{\azn}\left[bY_1(b\azn) - aY_1(a\azn)\right] \left[hJ_m(a\azn)-\azn + J_m'(a\azn)\right] +\end{align} + +and $C_0(r,\azn)$, $F(\azn)$ and $\azn$ are the same as above with $m=0$. + +\paragraph{Propensity function} +Todo. +% Not in my notes (Thomas). + +%\begin{align} +% q_{r=a}(t) = k_+ v|_{r=a} = +%\end{align} +%\begin{multline} +% q_{z=L}(t) = \left.- D_3 \pd{v}{z}\right|_{z=L} = +%\end{multline} + + + + + diff --git a/doc/greens_functions/tex/include/include.tex b/doc/greens_functions/tex/include/include.tex new file mode 100644 index 00000000..755dfe88 --- /dev/null +++ b/doc/greens_functions/tex/include/include.tex @@ -0,0 +1,27 @@ + +\author{Thomas Miedema (miedema@amolf.nl)\\Thomas Sokolowski (sokolowski@amolf.nl)} +\date{} + +\usepackage{amsmath} + +\numberwithin{equation}{section} %nice equation numbers +\usepackage{verbatim} %multiline comments +\usepackage{graphicx} +\usepackage{algorithmic} +\usepackage{algorithm} + +\hbadness 10000 % Disable underfull hbox warnings. + + + +\newcommand{\pd}[2]{\frac{\partial #1}{\partial #2}} +\renewcommand{\a}{\alpha} +\newcommand{\an}{\alpha_n} +\newcommand{\amn}{\alpha_{mn}} +\newcommand{\azn}{\alpha_{0n}} % alpha zero n +\renewcommand{\t}{\theta} +\newcommand{\pdd}[2]{\frac{\partial^{2} #1}{\partial #2^{2}}} +\newcommand{\ora}{\overrightarrow} + +\DeclareMathOperator{\erf}{erf} +\DeclareMathOperator{\erfc}{erfc} diff --git a/doc/greens_functions/tex/include/subSectionOnly.tex b/doc/greens_functions/tex/include/subSectionOnly.tex new file mode 100644 index 00000000..2c442cab --- /dev/null +++ b/doc/greens_functions/tex/include/subSectionOnly.tex @@ -0,0 +1,27 @@ +\usepackage{fancyhdr} + +\pagestyle{fancy} %fancyplain +\lhead{} +\chead{} +\rhead{} +\renewcommand{\headrulewidth}{0pt} +\cfoot{} +% Name instead of page numbers. +\fancyfoot[RO]{\small Th. Miedema (miedema@amolf.nl), Th. Sokolowski (sokolowski@amolf.nl)} + + +% Disables chapter, section and subsection numbering. +\setcounter{secnumdepth}{-1} + +% Disables numbering of equations. +\nonumber + + + + + + + + +% GRAVEYARD +%\renewcommand{\align}{\align*} %Doesn't do anything! diff --git a/doc/pseudocode/Makefile b/doc/pseudocode/Makefile new file mode 100644 index 00000000..5536a9c9 --- /dev/null +++ b/doc/pseudocode/Makefile @@ -0,0 +1,15 @@ + +# sudo yum install python-pygments + +all: pseudocode diagrams + +pseudocode: + pygmentize -l python -f html -O full -o pseudocode.html pseudocode.py + +.PHONY: diagrams +diagrams: + cd diagrams && make + +clean: + rm -rf pseudocode.html + cd diagrams && make clean diff --git a/doc/pseudocode/diagrams.html b/doc/pseudocode/diagrams.html new file mode 100644 index 00000000..9a4fdde8 --- /dev/null +++ b/doc/pseudocode/diagrams.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/doc/pseudocode/diagrams/Makefile b/doc/pseudocode/diagrams/Makefile new file mode 100644 index 00000000..4ba1126e --- /dev/null +++ b/doc/pseudocode/diagrams/Makefile @@ -0,0 +1,22 @@ + +# sudo yum install graphviz + +all: egfrd fireSingle firePair fireMulti createMulti + +egfrd: + dot egfrd.dot -Tpng -o egfrd.png + +fireSingle: + dot fireSingle.dot -Tpng -o fireSingle.png + +firePair: + dot firePair.dot -Tpng -o firePair.png + +fireMulti: + dot fireMulti.dot -Tpng -o fireMulti.png + +createMulti: + dot createMulti.dot -Tpng -o createMulti.png + +clean: + rm -rf *.png diff --git a/doc/pseudocode/diagrams/createMulti.dot b/doc/pseudocode/diagrams/createMulti.dot new file mode 100644 index 00000000..a352c7eb --- /dev/null +++ b/doc/pseudocode/diagrams/createMulti.dot @@ -0,0 +1,23 @@ + +// dot createMulti.dot -Tpng -o createMulti.png + +digraph createMulti { + +edge [color=blue]; +node [color=red]; + +createMulti -> addToMultiRecursive +createMulti -> addToMultiRecursive +createMulti -> addToMultiRecursive [label=" neighbors"] + +addToMultiRecursive -> "type?" + +"type?" -> single +single -> addToMulti +single -> burstNonMultis +single -> addToMultiRecursive2 +addToMultiRecursive2 [label="addToMultiRecursive"] + +"type?" -> multi +multi -> mergeMultis +} diff --git a/doc/pseudocode/diagrams/egfrd.dot b/doc/pseudocode/diagrams/egfrd.dot new file mode 100644 index 00000000..3f49720e --- /dev/null +++ b/doc/pseudocode/diagrams/egfrd.dot @@ -0,0 +1,29 @@ + +// http://linuxdevcenter.com/pub/a/linux/2004/05/06/graphviz_dot.html +// dot egfrd.dot -Tpng -o egfrd.png + +// Directed graph. +digraph egfrd { + +edge [color=blue]; +node [color=red]; + +EGFRDSimulator [shape=box] + +EGFRDSimulator -> initialize +EGFRDSimulator -> step + +initialize -> createSingle +initialize -> createSingle +initialize -> createSingle + +step -> scheduler + +scheduler -> "topEvent" + +"topEvent" -> fireSingle [label="single?"] +"topEvent" -> firePair [label=" pair?"] +"topEvent" -> fireMulti [label="multi?"] + +} + diff --git a/doc/pseudocode/diagrams/fireMulti.dot b/doc/pseudocode/diagrams/fireMulti.dot new file mode 100644 index 00000000..63e006fc --- /dev/null +++ b/doc/pseudocode/diagrams/fireMulti.dot @@ -0,0 +1,15 @@ +// dot fireMulti.dot -Tpng -o fireMulti.png + +digraph fireMulti { + +edge [color=blue]; +node [color=red]; + +fireMulti -> "bd step (not discussed)" +fireMulti -> burstMulti [label="reaction\nor escape?"] + +burstMulti -> createSingle +burstMulti -> createSingle +burstMulti -> createSingle +} + diff --git a/doc/pseudocode/diagrams/firePair.dot b/doc/pseudocode/diagrams/firePair.dot new file mode 100644 index 00000000..da1ebf98 --- /dev/null +++ b/doc/pseudocode/diagrams/firePair.dot @@ -0,0 +1,42 @@ +// dot firePair.dot -Tpng -o firePair.png + +digraph firePair { + +edge [color=blue]; +node [color=red]; + +firePair -> "eventType?" + +"eventType?" -> "Single Reaction" +"eventType?" -> "Pair Reaction" +"eventType?" -> Escape + +"Single Reaction" -> burstPair +burstPair -> propagatePair1 +propagatePair1 [label=propagatePair] +propagatePair1 -> drawNewPositions1 +drawNewPositions1 [label=drawNewPositions] +drawNewPositions1 -> drawNewCoM1 +drawNewCoM1 [label=drawNewCoM] +drawNewPositions1 -> drawNewIV1 +drawNewIV1 [label=drawNewIV] + + +"Single Reaction" -> fireSingleReaction +fireSingleReaction -> createSingle [label=" product?"] + +"Pair Reaction" -> drawNewCoM +"Pair Reaction" -> createSingle1 +createSingle1 [label=createSingle] + + +Escape -> propagatePair2 +propagatePair2 [label=propagatePair] +propagatePair2 -> drawNewPositions2 +drawNewPositions2 [label=drawNewPositions] +drawNewPositions2 -> drawNewCoM2 +drawNewCoM2 [label=drawNewCoM] +drawNewPositions2 -> drawNewIV2 +drawNewIV2 [label=drawNewIV] +} + diff --git a/doc/pseudocode/diagrams/fireSingle.dot b/doc/pseudocode/diagrams/fireSingle.dot new file mode 100644 index 00000000..a5acf4f1 --- /dev/null +++ b/doc/pseudocode/diagrams/fireSingle.dot @@ -0,0 +1,38 @@ +// dot fireSingle.dot -Tpng -o fireSingle.png + +digraph fireSingle { + +edge [color=blue]; +node [color=red]; + +fireSingle -> "eventType?" + +"eventType?" -> "Single Reaction" +"eventType?" -> Else + +"Single Reaction" -> propagateSingle1 +propagateSingle1 [label=propagateSingle] +propagateSingle1 -> drawNewPosition1 +drawNewPosition1 [label=drawNewPosition] +"Single Reaction" -> fireSingleReaction +fireSingleReaction -> createSingle1 [label=" product?"] +createSingle1 [label=createSingle] + +Else -> propagateSingle2 +propagateSingle2 [label=propagateSingle] +propagateSingle2 -> drawNewPosition2 +drawNewPosition2 [label=drawNewPosition] + +Else -> eventType2 +eventType2 [label="eventType?"] + +eventType2 -> Interaction +Interaction -> fireSingleReaction2 +fireSingleReaction2 [label=fireSingleReaction] +fireSingleReaction2 -> createSingle2 [label=" product?"] +createSingle2 [label=createSingle] + +eventType2 -> Escape +Escape -> "See code" +} + diff --git a/doc/pseudocode/pseudocode.py b/doc/pseudocode/pseudocode.py new file mode 100644 index 00000000..221dbbfc --- /dev/null +++ b/doc/pseudocode/pseudocode.py @@ -0,0 +1,265 @@ +class Single: + """ A Single contains: + - 1 Particle of a certain Species + - 1 shell (Sphere or Cylinder) + - 1 Domain (Cartesian or Radial, wrapper around appropriate GF) + - public methods determineNextEvent and drawNewPosition + + """ + + +class Pair: + """A pair contains: + - 2 Particles + - 1 shell (Sphere or Cylinder) + - 1 domain for center of mass (Cartesian or Radial) + - 1 domain for interparticle vector (Composite: r and theta) + - public methods determineNextEvent, drawNewPositions, drawNewCoM + + """ + + +class EGFRDSimulator: + """An EGFRDSimulator stores information about: + + surfaces: + - surfaceList + + types of particles that can exist on each surface: + - speciesList + + possible reactions each species can undergo: + - reactionTypeMap1 (for the monomolecular reactions) + - reactionTypeMap2 (for the bimolecular reactions) + - interactionTypeMap (for the particle-surface interactions) + + position of each particle: + - particleMatrix + + position and size of each shell: + - sphereMatrix (for the spherical shells) + - cylinderMatrix (for the cylindrical shells) + + order of events: + - scheduler (contains Singles, Pairs and Multis) + + """ + def initialize: + for particle in all particles: + createSingle( particle ) + + + def step: + # The event scheduler calls fireSingle, firePair or fireMulti, + # depending on the type of the top event. + + + + # singles + + def createSingle( particle ): + # Create single with dt=0. The surface the particle is on determines + # the type of the single (CylindricalSurfaceSingle, + # PlanarSurfaceSingle, SphericalSingle). + + + def createInteraction( single, surface ): + # Create interaction single including the particle and the surface. + # The surface the particle is on determines the type of the + # interaction single (CylindricalSurfaceInteraction, + # PlanarSurfaceInteraction). + + + def fireSingle( single ): + if eventType == REACTION: + propagateSingle( single ) + fireSingleReaction( single ) + return + if eventType == INTERACTION: + propagateSingle( single ) + # The reactionType of the single distinguishes this case from the + # above in fireSingleReaction. + fireSingleReaction( single ) + return + + # ESCAPE + propagateSingle( single ) + + # Find neighbors within burstVolume, and closestObject outside of + # burstVolume. BurstVolume depends on the surface the particle is on. + # Spherical by default. + ( neighbors, closestObject, distanceToShellOfClosestObject ) = + getNeighbors( position, burstVolume ) + + if neighbors: + burstedSingles = burstNonMultis( neighbor ) + if tryInteractionOrPairOrMulti( single, bursted ): + return + else: + # If nothing was formed, recheck closest and restore shells. + ( closestObject, distanceToShellOfClosestObject ) = + getClosestObj( position ) + + updateSingle( single, closestObject, distanceToShellOfClosestObject ) + + # Avoid livelock. + restoreSingleShells( burstedSingles ) + + + def tryInteractionOrPairOrMulti( single1, neighbors ): + # Try to make an interaction or a pair with single1 and the closest + # neighbor. If single1 and closest neighbor are too far apart, do + # nothing. If more than 2 objects are close together, make a multi. + + + def burstSingle( single ): + # There is a technical difference, not mentioned now. + propagateSingle( single ) + + + def propagateSingle( single ): + drawNewPosition() + # If the single is an interaction single, remove it and create a + # normal single at the same position using createSingle(). + + + def restoreSingleShells( singles ): + for single in singles: + ( closestObject, distanceToShellOfClosestObject ) = getClosestObj() + updateSingle( single, closestObject, + distanceToShellOfClosestObject ) + + + def updateSingle( single, closestObject, distanceToShellOfClosestObject ): + if closestObject is Single: + radiusOfSingle = + calculateSingleShellSize( single, closestObject, + distanceToShellOfClosestObject ) + else: + # closestObject is Pair or Multi or Surface. + # It is harder to determine the optimal shellSize. Make single as + # big as possible for now. + radiusOfSingle = distanceToShellOfClosestObject + + ( dt, eventType ) = determineNextEvent( ) + + + def calculateSingleShellSize( single, single2, + distanceToShellOfSingle2 ): + # Optimal shellSize is half of the distance between the 2 particles if + # the radii and the diffusion constants are the same. + return min( radius1 + sqrtD1 / ( sqrtD1 + sqrtD2 ) * + ( distanceBetweenParticles - + ( radius1 + radius2 ) ), + distanceToShellOfSingle2 ) + + + def fireSingleReaction( single ): + if number of products is 1: + if reactionType is SurfaceUnbindingReactionType: + newpos = randomUnbindingSite( oldpos ) + elif reactionType is SurfaceBindingReactionType: + newpos = projectedPoint( oldpos ) + else: + # A -> B, no surfaces involved. + newpos = oldpos + + createSingle( productParticle ) # at newpos + + elif number of products is 2: + # A -> B + C, no surfaces involved. + # Draw a random vector of a fixed length. The orientation depends + # on the surface the particle is on. + length = ( particleRadius1 + particleRadius2 ) * + MINIMAL_SEPERATION_FACTOR ) + vector = randomVector( length ) + + newpos1 = oldpos + vector * ( D1 / D12 ) + newpos2 = oldpos - vector * ( D2 / D12 ) + + createSingle( particle1 ) # at newpos1 + createSingle( particle2 ) # at newpos2 + + + + # pairs + + def createPair( single1, single2, shellSize ) + # Create pair around the CoM of particle1 and particle2 with shellSize + # as radius. The surface the particles are on determines the type of + # the pair (CylindricalSurfacePair, PlanarSurfacePair, SphericalPair). + + + def firePair( pair ): + if eventType == SINGLE_REACTION: + burstPair( pair ) + fireSingleReaction( reactingsingle ) + elif eventType == PAIR_REACTION: + newCenterOfMass = drawNewCoM() + createSingle( productParticle ) # at newCenterOfMass + else: + # Escape through either r or R. + propagatePair( pair ) + + + def burstPair( pair ): + # There is a technical difference, not mentioned now. + propagatePair( pair ) + + + def propagatePair( pair ): + drawNewPositions() # for both particles. + + + + # multis + + def createMulti( single, neighbors ): + # Create multi and add the single and all objects in the neighbors + # list recursively. A multi has it's own Brownian Dynamics simulator. + addToMulti( single, multi ) + for neighbor in neighbors: + addToMultiRecursive( neighbor, multi ) + + + def addToMultiRecursive( thisObject, multi ): + if thisObject is Single: + addToMulti( thisObject, multi ) + # First burst neighboring objects that have a shell within + # MULTI_SHELL_FACTOR * radius of thisObject. + neighbors = burstNonMulti( neighbors ) + # Then select only those neighbors who have a particle that is + # within MULTI_SHELL_FACTOR * radius of thisObject. + (no pseudocode here) + # Finally, add those neighbors recursively. + for neighbor in neighbors: + addToMultiRecursive( neighbor, multi ) + else: + mergeMultis( thisObject, multi ) + + + def addToMulti( single, multi ): + addParticle( particle ) + # Add a static shell with a radius that is the radius of the particle + # multiplied by MULTI_SHELL_FACTOR. When the particle leaves this + # shell, the multi is bursted (see fireMulti). + shellSize = radiusOfParticle * MULTI_SHELL_FACTOR + addShell( position, shellSize ) + + + def mergeMultis( multi1, multi2 ): + #Merge multi1 into multi2. + + + def fireMulti( multi ): + # Do one execution step of the Brownian Dynamics simulator. Not + # further discussed here. + + if reaction occured or particle escaped: + burstMulti( multi ) + + + def burstMulti( multi ): + for particle in all multi particles: + createSingle( particle ) + diff --git a/doc/visualization.txt b/doc/visualization.txt new file mode 100644 index 00000000..b13c9553 --- /dev/null +++ b/doc/visualization.txt @@ -0,0 +1,158 @@ +Visualization +============= + +Notes +----- +* Right now this is not real-time. First you run your simulation, then you use + Paraview to visualize it. + + +Setting up the simulation +------------------------- +### Option 1 +Let a vtklogger write visualization data for the last 100 steps. + +* Setup the vtklogger like this in your simulation script (also see + /example/example.py): + + {% highlight python %} + + s.initialize() + vtklogger = VTKLogger( s, 'myVisualizationDataDirectory', 100 ) + while(True): + try: + vtklogger.log() + s.step() + except RuntimeError, message: + print 'Error.', message + break + vtklogger.stop() + s.stop( s.t ) + + {% endhighlight %} + +* Replace 'myVisualizationDataDirectory' by a descriptive name for each + simulation that you run. It can be a nested directory structure, like + 'data/10000steps/run1', and is automatically created. + +* This stores all data in a buffer during the simulation, and only writes the + last 100 steps after the simulation has finished. + +### Option 2 +Let a vtklogger write visualization data for all steps. + +* This is done the same way as option 1, except remove the last argument to + VTKLogger. + +* This writes the visualization data files directly during the simulation. See + below. + + + + +Running the simulation +---------------------- +* Run the simulation script. + + PYTHONPATH=../../ python run.py + +* This should produce the files static.pvd and files.pvd inside the + visualization data directory specified in your simulation script. The former + contains static information about the surfaces you defined in your + simulation. The latter contains a list of references to .vtu files. For each + timestep there is a .vtu file that contains information about the particles, + a .vtu file for the sphericle shells, and a .vtu file for all the + cylindrical shells. + +* When not using a buffer, i.e. option 2, the .vtu files are created during + the simulation. However, we have to wait for the simulation to finish before + we can visualize the data using Paraview. It is not till the call to + vtklogger.stop() that files.pvd is created with an overview of all .vtu + files. This is the reason that the visualization can not be shown real-time + at the moment. + + + + +Configuring Paraview +-------------------- +* Install Paraview. http://paraview.org/ + +* Copy the plugins /paraview/tensorGlyph.xml and + /paraview/tensorGlyphCustomSource.xml to + /home//.config/ParaView/ParaView/Plugins/ + replacing by the version of your Paraview installation (for + example 3.6). These 2 files are needed to show cylinders of different sizes + and different orientations correctly. + +* In /paraview/pipeline.pvsm search for and replace + /full/path/to/some/files.pvd and /full/path/to/some/static.pvd by the full + paths to those files that were created by the simulation. So you should get + something like: + /home///simulations///files.pvd + and + /home///simulations///static.pvd. + + Note that Paraview will crash if these files don't exist. And they have are + to be absolute paths, using ~ for home directory, or ./../ for the egfrd + directory doesn't seem to work. This means Paraview will also crash if you + move your egfrd directory or something at some later point. + +* Start Paraview. + +* Go to Tools > Manage Plugins/Extensions and check that the plugins are + loaded, or load them manually. + + Note that Paraview will crash if the plugins are not loaded when you try + to load the pipeline in the next step. + + + + +Visualizing the data +-------------------- +* Go to File > Load State, and select /paraview/pipeline.pvsm. + +* Click the play button. You should see an animation of your simulation. Yeah! + + + + +Tweaking the pipeline +--------------------- +* From now on you can make changes to the pipeline from inside Paraview. Here + is an example of what to do when you want to use data from a completely + different simulation. + - Right click in the Pipeline Browswer window. Open files.pvd in the + visualization data directory of the new simulation. Then click apply in + the Object Inspector window. The new files.pvd is now added to the + pipeline, but doesn't do anything yet. + - Left click on the 'plus' next to files.pvd in the pipeline browser, then + right click on Objects below files.pvd, then Change Input. In the middle + window that appears, called Select Sources, select the new files.pvd file + you just added. It might be a bit hard to find. Click Ok. Objects is now + rewired to use the new files.pvd. + - Right click the old files.pvd, and delete it. Note that this is only + possible if you actually rewired Objects to a new files.pvd file in the + previous step, otherwise this options is grayed out. + - If the definition of the surfaces also changed: open and apply the new + static.pvd, rewire Surfaces to it and delete the old static.pvd. + - Save the new pipeline. File > Save State. + +* If you run a new simulation that writes to the same visualization data + directory, usually Paraview will reload the new data correctly. There can be + a problem if you increase the number of timesteps. If so, open the new + files.pvd into the pipeline and rewire Objects as explained above. + +* Different species are given different colors. If you run a new simulation + that has many more species, you might need to rescale the color range. Select + Particle in the Pipeline Browser window. Then in the Object Inspector window + click the middle tab called Display. Click Rescale to Data Range in the + Color subsection. + Typically you don't want to rescale the colors for Sphere and Cylinder. + Pairs are given a different color (2) than Singles (1) by default. The + sphere or cylinder that will be updated next is highlighed (color 0). + + + + From 74eaf42d928ea5c9b34c158816a5b0864b76956a Mon Sep 17 00:00:00 2001 From: Thomas Miedema Date: Wed, 4 May 2011 01:02:54 +0200 Subject: [PATCH 0015/1541] Use repr() for printing time, str() truncates floats When time gets truncated, ParaView thinks nothing has changed. --- visualization/vtk_xml_serial_unstructured.py | 4 +++- visualization/vtklogger.py | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/visualization/vtk_xml_serial_unstructured.py b/visualization/vtk_xml_serial_unstructured.py index ff3b6886..b5ef9fc6 100644 --- a/visualization/vtk_xml_serial_unstructured.py +++ b/visualization/vtk_xml_serial_unstructured.py @@ -230,7 +230,9 @@ def write_pvd(self, file, file_list): # Problem with adding real time can be that # TimestepValues is not updated in Proxy group="misc" # in .pvsm file after a reload. - time = str(time) + # Note: use repr(time), since str(time) truncates floating + # points. Alternatively: http://labix.org/python-nicefloat. + time = repr(time) data_set.setAttribute("timestep", time) data_set.setAttribute("group", "") data_set.setAttribute("part", type) # Use Extractblock. diff --git a/visualization/vtklogger.py b/visualization/vtklogger.py index 41743d52..aec85800 100644 --- a/visualization/vtklogger.py +++ b/visualization/vtklogger.py @@ -85,8 +85,16 @@ def __init__(self, sim, dir='vtkdata', buffer_size=None, show_shells=True, # Step counter. self.i = 0 - # Needed for ParaView time hack. Note: don't make delta_t too small. - self.delta_t = 1e-10 + # Needed for ParaView time hack. + # + # The result should be that with extra_particle_step=True, the number + # of Paraview timesteps should be twice the number of simulation steps. + # + # delta_t should not be smaller than the precision of your + # floating point output (use repr, not str). But it should be smaller + # than the first simulation timestep divided by the number of initial + # particles. 1e-15 seems to be a good value. + self.delta_t = 1e-15 self.last_time = INF def cleanup(self): @@ -117,6 +125,9 @@ def log(self): # # Now ParaView should perceive a state change. # Only needed when showing shells (this is a choice). + # + # A relative offset is needed when last_time > 1, and an absolute + # offset when last_time < 1. time = self.last_time * (1 + self.delta_t) + self.delta_t # Get data. @@ -168,7 +179,7 @@ def writelog(self, time, index, self.make_snapshot('cylinder_data', cylinder_data_1, index, time) # Continue with case. - time *= (1 + self.delta_t / 2) + time = time * (1 + self.delta_t / 2.) + self.delta_t / 2. index += 1 if index == 0: From 886c19ad778bcb34da32eaae6ccaaafadd917cd8 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Wed, 4 May 2011 12:30:45 +0200 Subject: [PATCH 0016/1541] This is merely a simple test, with an edited comment, which should be edited out. --- samples/reversible/run.py | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/reversible/run.py b/samples/reversible/run.py index 8df2696d..4a9ab466 100644 --- a/samples/reversible/run.py +++ b/samples/reversible/run.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +#blabla ''' LOGLEVEL=ERROR PYTHONPATH=../.. python -O run.py rev.3.out 1.25e-2 5000000 & From 6a65f9f4065a78a1e7d262cef9978c06983e1cbe Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Wed, 4 May 2011 12:35:28 +0200 Subject: [PATCH 0017/1541] Removed the previously edited useless comment (which was only for testing). --- samples/reversible/run.py | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/reversible/run.py b/samples/reversible/run.py index 4a9ab466..8df2696d 100644 --- a/samples/reversible/run.py +++ b/samples/reversible/run.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -#blabla ''' LOGLEVEL=ERROR PYTHONPATH=../.. python -O run.py rev.3.out 1.25e-2 5000000 & From 9ca2669a8199b9deca72b2b706546198b177f891 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Wed, 4 May 2011 14:15:59 +0200 Subject: [PATCH 0018/1541] working on samples directory (bd_test) --- samples/bd_test/irr.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/samples/bd_test/irr.py b/samples/bd_test/irr.py index 8d10ef2c..016bc835 100644 --- a/samples/bd_test/irr.py +++ b/samples/bd_test/irr.py @@ -2,6 +2,8 @@ from bd import * import sys +import gfrdbase +import model def run(outfilename, T, N): @@ -19,21 +21,24 @@ def run(outfilename, T, N): def singlerun(T): - w = World(1e-3, 3) - s = BDSimulator(w) - sigma = 5e-9 r0 = sigma D = 1e-12 kf = 10 * sigma * D - m = ParticleModel() + m = model.ParticleModel(world_size=1e-5) - A = m.new_species_type('A', D/2, sigma/2) - B = m.new_species_type('B', D/2, sigma/2) - C = m.new_species_type('C', D/2, sigma/2) + A = model.Species('A', D/2, sigma/2) + B = model.Species('B', D/2, sigma/2) + C = model.Species('C', D/2, sigma/2) + # Couple these species to the previously defined particle model + m.add_species_type(A) # "m.add_species_type(species=A)" not + # allowed due boost package + m.add_species_type(B) + m.add_species_type(C) - r1 = create_binding_reaction_rule(A, B, C, kf) + r1 = model.create_binding_reaction_rule(A, B, C, kf) + m.network_rules.add_reaction_rule(r1) s.set_model(m) @@ -42,6 +47,14 @@ def singlerun(T): particleB = s.place_particle(B, [(float(A['radius']) + float(B['radius']))+1e-23,0,0]) end_time = T + + # Create world size m, with matrix_size sized + # neighbour matrix +# w = gfrdbase.create_world(m=1e-3, matrix_size=3) + w = gfrdbase.create_world(1e-3, 3) + s = BDSimulator(w) + + #s.initialize() while 1: From ca55c5d013d9a4bf98e3327f56ad8bedbb93fb55 Mon Sep 17 00:00:00 2001 From: Laurens Bossen Date: Wed, 4 May 2011 14:51:10 +0200 Subject: [PATCH 0019/1541] Fixed missing folders for latex files in doc directory. --- doc/greens_functions/build/include/.empty-dir | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/greens_functions/build/include/.empty-dir diff --git a/doc/greens_functions/build/include/.empty-dir b/doc/greens_functions/build/include/.empty-dir new file mode 100644 index 00000000..e69de29b From e0542ad6c08dc3093b823791022dae14f04880c1 Mon Sep 17 00:00:00 2001 From: Thomas Miedema Date: Sat, 30 Apr 2011 05:16:58 +0800 Subject: [PATCH 0020/1541] Visualize extra particle for arbitrary timesteps Tricking Paraview into showing 2 steps for each simulator step. --- visualization/vtklogger.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/visualization/vtklogger.py b/visualization/vtklogger.py index 0a1ee26a..41743d52 100644 --- a/visualization/vtklogger.py +++ b/visualization/vtklogger.py @@ -85,9 +85,8 @@ def __init__(self, sim, dir='vtkdata', buffer_size=None, show_shells=True, # Step counter. self.i = 0 - # Needed for ParaView time hack. Note: don't make delta_t too - # small, should be relative to max time. - self.delta_t = 1e-11 + # Needed for ParaView time hack. Note: don't make delta_t too small. + self.delta_t = 1e-10 self.last_time = INF def cleanup(self): @@ -118,7 +117,7 @@ def log(self): # # Now ParaView should perceive a state change. # Only needed when showing shells (this is a choice). - time = self.last_time + self.delta_t + time = self.last_time * (1 + self.delta_t) + self.delta_t # Get data. particle_data = self.get_particle_data() @@ -169,7 +168,7 @@ def writelog(self, time, index, self.make_snapshot('cylinder_data', cylinder_data_1, index, time) # Continue with case. - time += self.delta_t / 2 + time *= (1 + self.delta_t / 2) index += 1 if index == 0: From 8f24f8b5d22927547e8b039964c4da1eac980b75 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Mon, 9 May 2011 16:17:39 +0200 Subject: [PATCH 0021/1541] Fixed the BD_test sample irr.py. But also wrecked it again. I converted the code to the newer syntax, and also added stuff that seemed necessary to make it do what it is supposed to. Also, I've added the option to switch between the eGFRD simulator and Brownian dynamics via bash input arguments. When you run it as follows: $ python irr.py out.txt 1 1 GF there are no problems. However if you make it run more than once, it does give problems (e.g.: $ python irr.py out.txt 1 2 GF ) This is particularly curious because the 2 means that a function is called twice. But the arguments given to the function don't change. Thus it shouldn't matter if the simulation is performed once or twice. Somehow it does however. Anyone? For more comments see the code itself. modified: irr.py --- samples/bd_test/irr.py | 137 +++++++++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 33 deletions(-) diff --git a/samples/bd_test/irr.py b/samples/bd_test/irr.py index 016bc835..8c2cff11 100644 --- a/samples/bd_test/irr.py +++ b/samples/bd_test/irr.py @@ -1,16 +1,57 @@ #!/usr/bin/env python +""" +Brownian Dynamics test script + +Does the same as the irreversible example, +but allows for forcing the script to use +Brownian Dynamics (BD) only. + +Script is programmed in such a way that +choosing between BD and normal eGFRD (which +includes BD) can be done by giving "BD" +(Brownian Dynamics) or "GF" (Green's Functions) +as first bash argument: + +$ python irr.py [outputfile name] [time reaction is + allowed to run] [number of runs] [BD/GF] + +e.g.: +$ python irr.py out.txt 1e-4 100 GF + +""" + +# import necessary libraries -from bd import * import sys +# Tell Python where the main .py files are. +sys.path.append('../../') +# Alternatively one could use the bash command: +# $ export PYTHONPATH=../../ + +from bd import * import gfrdbase import model +import myrandom +import _gfrd + +from egfrd import * -def run(outfilename, T, N): + +def run(outfilename, T, N, which_sim): + """ + Simply runs simulation N times. + + Arguments: + - outfilename : in which file to put output + - T : time single simulation is allowed to run + - N : number of times single sim is performed + - which_sim : whether to use eGFRD or BD simulator + """ outfile = open(outfilename, 'w') for i in range(N): - d, t = singlerun(T) + d, t = singlerun(T, which_sim) outfile.write('%g\n' % d) print d, t @@ -19,66 +60,96 @@ def run(outfilename, T, N): outfile.close() -def singlerun(T): +def singlerun(T, which_sim): + """ + Performs a simulation that involves calculating the reaction time + between two particles that are released next to each other. + Input arguments: + - T: Time after which simulation is ended no matter what. + - which_sim : whether to use eGFRD or BD simulator + """ + + # Some constants sigma = 5e-9 r0 = sigma D = 1e-12 kf = 10 * sigma * D + # ### Set up model m = model.ParticleModel(world_size=1e-5) A = model.Species('A', D/2, sigma/2) B = model.Species('B', D/2, sigma/2) C = model.Species('C', D/2, sigma/2) + # Couple these species to the previously defined particle model m.add_species_type(A) # "m.add_species_type(species=A)" not # allowed due boost package m.add_species_type(B) m.add_species_type(C) + # Create reaction rules r1 = model.create_binding_reaction_rule(A, B, C, kf) - m.network_rules.add_reaction_rule(r1) - s.set_model(m) - - particleA = s.place_particle(A, [0,0,0]) - particleB = s.place_particle(B, [(float(A['radius']) + float(B['radius']))+1e-23,0,0]) - - end_time = T - - # Create world size m, with matrix_size sized - # neighbour matrix -# w = gfrdbase.create_world(m=1e-3, matrix_size=3) - w = gfrdbase.create_world(1e-3, 3) - s = BDSimulator(w) - - #s.initialize() + # ### Set up simulator + w = gfrdbase.create_world(m, 3) + + # Set up the chosen simulator with an if-statement + if (which_sim == "GF"): + s = EGFRDSimulator(w, myrandom.rng) + # EGFRDSimulator(self, world, + # rng=<_gfrd.RandomNumberGenerator + # object at 0xef5140>, network_rules=None) + elif (which_sim == "BD"): + s = BDSimulator(w, myrandom.rng,\ + _gfrd.NetworkRulesWrapper(w.model.network_rules)) + # BDSimulator isn't meant for user-friendly use, + # so setting it u requires the "network_rules" + # (present reaction rules), which should be parsed + # with the _gfrd.NetworkRulesWrapper(). + else: + print "Incorrect argument given for simulator choice. Quitting." + quit() + + # ### Place particles + particleA = gfrdbase.place_particle(w, A, [0,0,0]) + particleB = gfrdbase.place_particle(w, B, [(float(A['radius']) + float(B['radius']))+1e-23,0,0]) + # ### Run the simulation while 1: next_time = s.t + s.dt - if next_time > end_time: + if next_time > T: # stop if too much time has passed break - s.step() + s.step() # Perform simulation step + - if s.core.last_reaction: + if s.last_reaction: # stop if reaction has occured print 'reaction' return 0.0, s.t - distance = s.distance_between_particles(particleA[1], particleB[1]) + distance = s.distance_between_particles(particleA[1], particleB[1]) + # Give distance between particles + + return distance, s.t # return variables of interest: distance and time + # s.t gives the current time, time that has passed in the simulator. + +if __name__ == '__main__': + run(sys.argv[1], float(sys.argv[2]), int(sys.argv[3]), sys.argv[4]) + + + + + + + + + + + - return distance, s.t -def first(x): - x = iter(x) - try: - return x.next() - except StopIteration, e: - return None - -if __name__ == '__main__': - run(sys.argv[1], float(sys.argv[2]), int(sys.argv[3])) From e57ccf98f77e66ff0efd02d25a042e8cd047412b Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Tue, 10 May 2011 10:09:55 +0200 Subject: [PATCH 0022/1541] Nog wat geprobeerd te debuggen aan irr.py. modified: irr.py --- samples/bd_test/irr.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/samples/bd_test/irr.py b/samples/bd_test/irr.py index 8c2cff11..d3832a2e 100644 --- a/samples/bd_test/irr.py +++ b/samples/bd_test/irr.py @@ -136,10 +136,26 @@ def singlerun(T, which_sim): return distance, s.t # return variables of interest: distance and time # s.t gives the current time, time that has passed in the simulator. +# go if __name__ == '__main__': run(sys.argv[1], float(sys.argv[2]), int(sys.argv[3]), sys.argv[4]) +# Some debugging attempts: +""" +def print_vars(): + print "############" + for name in dir(): + myvalue = eval(name) + print name, "is", type(name), "and is equal to ", myvalue + +print_vars() +singlerun(float(sys.argv[2]), sys.argv[4]) +print_vars() +singlerun(float(sys.argv[2]), sys.argv[4]) +print_vars() +singlerun(float(sys.argv[2]), sys.argv[4]) +""" From fcb56879d38e666bddbf15c23f142e3445d55faa Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Tue, 10 May 2011 15:25:47 +0200 Subject: [PATCH 0023/1541] Deleted all files in bd_test. These files were outdated. Moreover, there was already a more convenient implementation of this functionality in the "irreversible" example. I'll add a README explaining this situation. --- samples/bd_test/data/.empty-dir | 0 samples/bd_test/irr.py | 171 -------------------------------- 2 files changed, 171 deletions(-) delete mode 100644 samples/bd_test/data/.empty-dir delete mode 100644 samples/bd_test/irr.py diff --git a/samples/bd_test/data/.empty-dir b/samples/bd_test/data/.empty-dir deleted file mode 100644 index e69de29b..00000000 diff --git a/samples/bd_test/irr.py b/samples/bd_test/irr.py deleted file mode 100644 index d3832a2e..00000000 --- a/samples/bd_test/irr.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env python -""" -Brownian Dynamics test script - -Does the same as the irreversible example, -but allows for forcing the script to use -Brownian Dynamics (BD) only. - -Script is programmed in such a way that -choosing between BD and normal eGFRD (which -includes BD) can be done by giving "BD" -(Brownian Dynamics) or "GF" (Green's Functions) -as first bash argument: - -$ python irr.py [outputfile name] [time reaction is - allowed to run] [number of runs] [BD/GF] - -e.g.: -$ python irr.py out.txt 1e-4 100 GF - -""" - -# import necessary libraries - -import sys -# Tell Python where the main .py files are. -sys.path.append('../../') -# Alternatively one could use the bash command: -# $ export PYTHONPATH=../../ - -from bd import * -import gfrdbase -import model -import myrandom -import _gfrd - -from egfrd import * - - -def run(outfilename, T, N, which_sim): - """ - Simply runs simulation N times. - - Arguments: - - outfilename : in which file to put output - - T : time single simulation is allowed to run - - N : number of times single sim is performed - - which_sim : whether to use eGFRD or BD simulator - """ - - outfile = open(outfilename, 'w') - - for i in range(N): - d, t = singlerun(T, which_sim) - outfile.write('%g\n' % d) - - print d, t - #assert d == 0 or t == T - - outfile.close() - - -def singlerun(T, which_sim): - """ - Performs a simulation that involves calculating the reaction time - between two particles that are released next to each other. - - Input arguments: - - T: Time after which simulation is ended no matter what. - - which_sim : whether to use eGFRD or BD simulator - """ - - # Some constants - sigma = 5e-9 - r0 = sigma - D = 1e-12 - kf = 10 * sigma * D - - # ### Set up model - m = model.ParticleModel(world_size=1e-5) - - A = model.Species('A', D/2, sigma/2) - B = model.Species('B', D/2, sigma/2) - C = model.Species('C', D/2, sigma/2) - - # Couple these species to the previously defined particle model - m.add_species_type(A) # "m.add_species_type(species=A)" not - # allowed due boost package - m.add_species_type(B) - m.add_species_type(C) - - # Create reaction rules - r1 = model.create_binding_reaction_rule(A, B, C, kf) - m.network_rules.add_reaction_rule(r1) - - - # ### Set up simulator - w = gfrdbase.create_world(m, 3) - - # Set up the chosen simulator with an if-statement - if (which_sim == "GF"): - s = EGFRDSimulator(w, myrandom.rng) - # EGFRDSimulator(self, world, - # rng=<_gfrd.RandomNumberGenerator - # object at 0xef5140>, network_rules=None) - elif (which_sim == "BD"): - s = BDSimulator(w, myrandom.rng,\ - _gfrd.NetworkRulesWrapper(w.model.network_rules)) - # BDSimulator isn't meant for user-friendly use, - # so setting it u requires the "network_rules" - # (present reaction rules), which should be parsed - # with the _gfrd.NetworkRulesWrapper(). - else: - print "Incorrect argument given for simulator choice. Quitting." - quit() - - # ### Place particles - particleA = gfrdbase.place_particle(w, A, [0,0,0]) - particleB = gfrdbase.place_particle(w, B, [(float(A['radius']) + float(B['radius']))+1e-23,0,0]) - - # ### Run the simulation - while 1: - next_time = s.t + s.dt - if next_time > T: # stop if too much time has passed - break - s.step() # Perform simulation step - - - if s.last_reaction: # stop if reaction has occured - print 'reaction' - return 0.0, s.t - - distance = s.distance_between_particles(particleA[1], particleB[1]) - # Give distance between particles - - return distance, s.t # return variables of interest: distance and time - # s.t gives the current time, time that has passed in the simulator. - -# go -if __name__ == '__main__': - run(sys.argv[1], float(sys.argv[2]), int(sys.argv[3]), sys.argv[4]) - - -# Some debugging attempts: -""" -def print_vars(): - print "############" - for name in dir(): - myvalue = eval(name) - print name, "is", type(name), "and is equal to ", myvalue - -print_vars() -singlerun(float(sys.argv[2]), sys.argv[4]) -print_vars() -singlerun(float(sys.argv[2]), sys.argv[4]) -print_vars() -singlerun(float(sys.argv[2]), sys.argv[4]) -""" - - - - - - - - - - - - - From c642f432f87b56af55270a9e721416de05f0a7d2 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Tue, 10 May 2011 15:29:53 +0200 Subject: [PATCH 0024/1541] Added README file promised in previous commit. --- samples/bd_test/README | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 samples/bd_test/README diff --git a/samples/bd_test/README b/samples/bd_test/README new file mode 100644 index 00000000..4b9ff75f --- /dev/null +++ b/samples/bd_test/README @@ -0,0 +1,11 @@ + + +Introduction +===================================== + +This folder will in the future hold scripts to test the Brownian +Dynamics part of the code. + +To test Brownian Dynamics, one is now referred to the "irreversible" +example. Which also contains a functionality to test Brownian +Dynamics. From e7830535a91a3dc5dc8df6217b8f2202cf0385df Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Wed, 11 May 2011 11:29:31 +0200 Subject: [PATCH 0025/1541] Some updates for the rebind sample, doesn't work yet, still working on it. modified: samples/rebind/run.py --- samples/rebind/run.py | 97 ++++++++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/samples/rebind/run.py b/samples/rebind/run.py index 3d1a56b9..d61837a4 100644 --- a/samples/rebind/run.py +++ b/samples/rebind/run.py @@ -11,6 +11,8 @@ from egfrd import * from bd import * import sys +import gfrdbase +import model def run(outfilename, D_factor, N_B, N_X, N): print outfilename @@ -58,6 +60,7 @@ def singlerun(T_list, D_factor, N_B, N_X): # V = 1.66e-21 m^3 # L = 1.18e-7 + # ### Define constants DX_factor = 1 V = 1e-18 # m^3 @@ -66,13 +69,6 @@ def singlerun(T_list, D_factor, N_B, N_X): matrix_size = min(max(3, int((9 * (N_X+N_B)) ** (1.0/3.0))), 60) print 'matrix_size=', matrix_size - w = World(L, matrix_size) - s = EGFRDSimulator(w) - #s.set_user_max_shell_size(1e-6) - #s = BDSimulator(w) - - box1 = CuboidalRegion([0,0,0],[L,L,L]) - radius = 2.5e-9 sigma = radius * 2 r0 = sigma @@ -82,22 +78,44 @@ def singlerun(T_list, D_factor, N_B, N_X): tau = sigma**2 / D_tot #kf = 1000 * sigma * D_tot - # 1e9 [1 / (M s)] -> 1e9 / 1000 / N_A [m^3 / s] kf = 0.092e-18 - m = ParticleModel() + DX = D * DX_factor + + # ### Define model + + # Create model class + m = model.ParticleModel(L) + + # Particle species classes + A = model.Species('A', D, radius) + m.add_species_type(A) + B = model.Species('B', D, radius) + m.add_species_type(B) + C = model.Species('C', D, radius) + m.add_species_type(C) + X = model.Species('X', DX, radius) + m.add_species_type(X) + + # Reaction rules + r1 = model.create_binding_reaction_rule(A, B, C, kf) + m.network_rules.add_reaction_rule(r1) - A = m.new_species_type('A', D, radius) - B = m.new_species_type('B', D, radius) - C = m.new_species_type('C', D, radius) + r2 = model.create_unbinding_reaction_rule(C, A, B, 1e3) + m.network_rules.add_reaction_rule(r2) - DX = D * DX_factor + # ### Set up simulator - X = m.new_species_type('X', DX, radius) + w = gfrdbase.create_world(L, matrix_size) + nrw = gfrdbase.create_network_rules_wrapper(m) + s = EGFRDSimulator(w, myrandom.rng, nrw) + # ### Place particles + + # Throw in some X particles and stirr if N_X != 0: - s.throw_in_particles(X, N_X, box1) + gfrdbase.throw_in_particles(w, X, N_X) end_time = tau * 1 while 1: @@ -109,18 +127,27 @@ def singlerun(T_list, D_factor, N_B, N_X): s.reset() - - r1 = create_binding_reaction_rule(A, B, C, kf) - m.network_rules.add_reaction_rule(r1) - - r2 = create_unbinding_reaction_rule(C, A, B, 1e3) - m.network_rules.add_reaction_rule(r2) - - s.set_model(m) + # Removed introduction of network rules here. + # s.set_model(m) # IF presence of network rules results in data + # that is not cleaned in reset, this might give + # problems. Originally, s.set_model(m) would + # have introduced these rules here. A_pos = [0,0,0] B_pos = [(float(A['radius']) + float(B['radius']))+1e-23,0,0] + # Clear an area at position A_pos and place particle A there + """ + What happens here: + - find particles at position A_pos within species A radius + - delete those + - add a number of X particles _randomly to the box_, the + number being equal to # removed particles. + - if a particle is accidently placed within the "cleared" + area, repeat the process (in very crowded box, this + leads to infinite loop) + - + """ while 1: pp = s.get_particles_within_radius(A_pos, float(A['radius'])) if not pp: @@ -131,6 +158,7 @@ def singlerun(T_list, D_factor, N_B, N_X): s.place_particle(A, A_pos) + # Idem for a particle B: while 1: pp = s.get_particles_within_radius(B_pos, float(B['radius'])) if not pp: @@ -141,6 +169,7 @@ def singlerun(T_list, D_factor, N_B, N_X): s.place_particle(B, B_pos) + # Place rest of B at random positions if N_B > 1: s.throw_in_particles(B, N_B-1, box1) @@ -150,12 +179,30 @@ def singlerun(T_list, D_factor, N_B, N_X): s.step() - next_stop = T_list[0] + next_stop = T_list[0] # In this case T_list[0] equals infinity i_T = 0 + #TODO (following correct?-:) + """ + What happens here: + - If there was a reaction + - And there are no C-particles, set t_last to current simulation + time + - And there are C particles, calculate dt between previous??? and + this reaction. + + OR: + + - If the last C particle has just reacted away, record the time + - If there are still C particles, record the time relative to this + occurence + + T-list in principle contains a list with scheduled "stops" + + """ while 1: - if s.last_reaction: + if s.last_reaction: # aka if there was a reaction print s.last_reaction if len(s.world.get_particle_ids(C.id)) == 0: #A,B print 'set t_last', s.t From f94b323a928c86fc101b2df22e8fb3ff1cdd661c Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Wed, 11 May 2011 11:31:19 +0200 Subject: [PATCH 0026/1541] Added template folder, which should in future contain file from which users can easily compose working script. --- samples/template/README | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 samples/template/README diff --git a/samples/template/README b/samples/template/README new file mode 100644 index 00000000..c98a176a --- /dev/null +++ b/samples/template/README @@ -0,0 +1,33 @@ + + +General lay-out of a simulation +===================================== + + # ### Import correct modules +import sys +from egfrd import * +from bd import * +import model +import gfrdbase +import myrandom + + # ### Define model + + m = model.ParticleModel(1e-3) + + A = model.Species('A', 0.0, sigma/2) + m.add_species_type(A) + + r1 = model.create_binding_reaction_rule(A, B, C, kf) + m.network_rules.add_reaction_rule(r1) + + # ### Set up simulator + + + w = gfrdbase.create_world(m, 3) + nrw = gfrdbase.create_network_rules_wrapper(m) + s = EGFRDSimulator(w, myrandom.rng, nrw) + + # ### Place particles + + # ### Run simulation From 252992f0906fa101903d36a22840b87e95744952 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Thu, 12 May 2011 18:31:25 +0200 Subject: [PATCH 0027/1541] Most importantly, added some docstrings to usefull "user-interface" functions. Also worked a very little bit on fixing the samples. modified: dumper.py modified: egfrd.py modified: gfrdbase.py modified: model.py modified: samples/rebind/run.py modified: samples/template/README --- dumper.py | 47 +++++++++++++++++++++++++++++++++++------ egfrd.py | 11 ++++++++++ gfrdbase.py | 22 +++++++++---------- model.py | 2 +- samples/rebind/run.py | 40 +++++++++++++++++------------------ samples/template/README | 27 ----------------------- 6 files changed, 82 insertions(+), 67 deletions(-) diff --git a/dumper.py b/dumper.py index 839764e2..4737fe86 100644 --- a/dumper.py +++ b/dumper.py @@ -66,7 +66,14 @@ def dump_species_names(sim): return ' '.join(get_species_names(sim)) def _get_species_type_by_name(sim, name): - #Helper. + """ Return the type of a species with a certain name + + Arguments: + - sim + an EGFRDSimulator + - name + species name + """ #TODO: Added by wehrens@AMOLF.nl; Please revise. for species_type in sim.world.model.species_types: if species_type['name'] == name: return species_type @@ -74,7 +81,21 @@ def _get_species_type_by_name(sim, name): raise RuntimeError('SpeciesType %s does not exist.' % (name)) def _get_particles_by_sid(sim, sid): - # Helper. + """ Return a generator (using "yield") to loop over (pid, particle). + + Arguments: + - sim + an EGFRDSimulator + - sid + ID of a species + + E.g.: + + myparticles = _get_particles_by_sid(sim, sid) + + for mypid, myparticle in myparticles: + print str(str(mypid), str(myparticle)) + """ #TODO: Added by wehrens@AMOLF.nl; Please revise. for pid in sim.world.get_particle_ids(sid): particle = sim.world.get_particle(pid)[1] yield (pid, particle) @@ -121,7 +142,16 @@ def dump_particles(sim, identifier=None): return '\n'.join((str(x) for x in get_particles(sim, identifier))) def _get_number_of_particles_by_sid(sim, sid): - # Helper. + """ + Returns the number of particles of a certain species. + + Arguments: + - sim + an EGFRDSimulator. + - sid + ID of a species + + """ #TODO: Added by wehrens@AMOLF.nl; Please revise. if isinstance(sim, EGFRDSimulator): return len(sim.world.get_particle_ids(sid)) else: @@ -174,7 +204,10 @@ def dump_number_of_particles(sim, identifier=None): def get_domains(egfrdsim): """Return an iterator over the protective domains in the simulator. - """ + Arguments: + - egfrdsim + an EGFRDSimulator + """ #TODO: Added by wehrens@AMOLF.nl; Please revise. for did, domain in egfrdsim.domains.iteritems(): shell_list = domain.shell_list pid_particle_pair_list = [] @@ -244,10 +277,10 @@ def get_reaction_rules(model_or_simulator): return reaction_rules_1, reaction_rules_2, repulsive_rules def _dump_reaction_rule(model, reaction_rule): - # Helper. Return ReactionRule as string. + """Helper. Return ReactionRule as string. - #ReactionRule.__str__ would be good, but we are actually getting a - #ReactionRuleInfo or ReactionRuleCache object. + ReactionRule.__str__ would be good, but we are actually getting a + ReactionRuleInfo or ReactionRuleCache object.""" buf = ('k=%.3g' % reaction_rule.k + ': ').ljust(15) for index, sid in enumerate(reaction_rule.reactants): if index != 0: diff --git a/egfrd.py b/egfrd.py index b3abadcf..ec6ce565 100644 --- a/egfrd.py +++ b/egfrd.py @@ -126,6 +126,10 @@ def get_matrix_cell_size(self): return self.containers[0].cell_size def get_next_time(self): + """ + Returns the time it will be when the next egfrd timestep + is completed. + """ #TODO: Added by wehrens@AMOLF.nl; Please revise. if self.scheduler.size == 0: return self.t @@ -142,6 +146,13 @@ def get_max_shell_size(self): self.user_max_shell_size) def reset(self): + """ + This function resets the "records" of the simulator. This means + the simulator time is reset, the step counter is reset, events + are reset, etc. + Can be for example usefull when users want to "stirr" the + simulation before starting the "real experiment". + """ #TODO: Added by wehrens@AMOLF.nl; Please revise. self.t = 0.0 self.dt = 0.0 self.step_counter = 0 diff --git a/gfrdbase.py b/gfrdbase.py index e4bf7a18..d62c038b 100644 --- a/gfrdbase.py +++ b/gfrdbase.py @@ -81,13 +81,13 @@ class NoSpace(Exception): pass def get_closest_surface(world, pos, ignore): - # Return - # - closest surface - # - distance to closest surface - # - # We can not use matrix_space, it would miss a surface if the - # origin of the surface would not be in the same or neighboring - # cells as pos. + """Return + - closest surface + - distance to closest surface + + We can not use matrix_space, it would miss a surface if the + origin of the surface would not be in the same or neighboring + cells as pos.""" surfaces_and_distances_to_surfaces = [] @@ -104,10 +104,10 @@ def get_closest_surface(world, pos, ignore): return None, numpy.inf def get_closest_surface_within_radius(world, pos, radius, ignore): - # Return: - # - surface within radius or None - # - closest surface (regardless of radius) - # - distance to closest surface + """Return: + - surface within radius or None + - closest surface (regardless of radius) + - distance to closest surface""" surface, distance = get_closest_surface(world, pos, ignore) if distance < radius: diff --git a/model.py b/model.py index 59dc00f0..192ab32b 100644 --- a/model.py +++ b/model.py @@ -19,7 +19,7 @@ ] -# Define _gfrd docstrigns here, much easier to format than in C++. +# Define _gfrd docstrings here, much easier to format than in C++. _gfrd.create_cuboidal_region.__doc__ = \ """create_cuboidal_region(id, corner, diagonal) diff --git a/samples/rebind/run.py b/samples/rebind/run.py index d61837a4..84b09a2b 100644 --- a/samples/rebind/run.py +++ b/samples/rebind/run.py @@ -1,11 +1,14 @@ #!/usr/bin/env python -''' +#TODO +""" +See README for a description + # D_factor N_B N_X N LOGLEVEL=ERROR PYTHONPATH=../.. python -O run.py 1 1 100 10 -''' +""" from egfrd import * @@ -107,7 +110,7 @@ def singlerun(T_list, D_factor, N_B, N_X): # ### Set up simulator - w = gfrdbase.create_world(L, matrix_size) + w = gfrdbase.create_world(m, matrix_size) nrw = gfrdbase.create_network_rules_wrapper(m) s = EGFRDSimulator(w, myrandom.rng, nrw) @@ -183,25 +186,20 @@ def singlerun(T_list, D_factor, N_B, N_X): i_T = 0 - #TODO (following correct?-:) - """ - What happens here: - - If there was a reaction - - And there are no C-particles, set t_last to current simulation - time - - And there are C particles, calculate dt between previous??? and - this reaction. - - OR: - - - If the last C particle has just reacted away, record the time - - If there are still C particles, record the time relative to this - occurence - - T-list in principle contains a list with scheduled "stops" - - """ + # ### Start simulating while 1: + """ + What happens in this if-statement: + Create a list of the times particles were in bound state. + + If there was a reaction: + - Binding: Indicated by the fact that there are no C-particles. + In this case: record the current time (s.t) to t_last. + - Unbinding: Indicated because this is the only other case when + a reaction has taken place. + In this case: record the time binding lasted, i.e. dt between + current time (s.t) and last reaction time (t_last). + """ if s.last_reaction: # aka if there was a reaction print s.last_reaction if len(s.world.get_particle_ids(C.id)) == 0: #A,B diff --git a/samples/template/README b/samples/template/README index c98a176a..3a85ba66 100644 --- a/samples/template/README +++ b/samples/template/README @@ -3,31 +3,4 @@ General lay-out of a simulation ===================================== - # ### Import correct modules -import sys -from egfrd import * -from bd import * -import model -import gfrdbase -import myrandom - # ### Define model - - m = model.ParticleModel(1e-3) - - A = model.Species('A', 0.0, sigma/2) - m.add_species_type(A) - - r1 = model.create_binding_reaction_rule(A, B, C, kf) - m.network_rules.add_reaction_rule(r1) - - # ### Set up simulator - - - w = gfrdbase.create_world(m, 3) - nrw = gfrdbase.create_network_rules_wrapper(m) - s = EGFRDSimulator(w, myrandom.rng, nrw) - - # ### Place particles - - # ### Run simulation From 518785c61cc254564454fa584964ab5224a8a8f1 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Fri, 13 May 2011 12:09:31 +0200 Subject: [PATCH 0028/1541] Added implementation_functions to the doc directory. --- doc/Makefile.am | 3 +- doc/interface_functions.aux | 34 ++ doc/interface_functions.log | 846 ++++++++++++++++++++++++++ doc/interface_functions.pdf | Bin 0 -> 92315 bytes doc/interface_functions.tex | 1107 ++++++++++++++++++++++++++++++++++ doc/interface_functions.tex~ | 1107 ++++++++++++++++++++++++++++++++++ doc/interface_functions.toc | 33 + samples/template/README | 29 + utils.py | 40 +- 9 files changed, 3178 insertions(+), 21 deletions(-) create mode 100644 doc/interface_functions.aux create mode 100644 doc/interface_functions.log create mode 100644 doc/interface_functions.pdf create mode 100644 doc/interface_functions.tex create mode 100644 doc/interface_functions.tex~ create mode 100644 doc/interface_functions.toc diff --git a/doc/Makefile.am b/doc/Makefile.am index 091794b5..44ad9b22 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -8,7 +8,8 @@ if HAVE_PDFLATEX # Compile and install; do not distribute. pdf_DATA = \ implementation_notes.pdf\ - p1_fp.pdf + p1_fp.pdf\ + interface_functions.pdf endif diff --git a/doc/interface_functions.aux b/doc/interface_functions.aux new file mode 100644 index 00000000..53196f5c --- /dev/null +++ b/doc/interface_functions.aux @@ -0,0 +1,34 @@ +\relax +\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}} +\@writefile{toc}{\contentsline {section}{\numberline {2}Functions}{1}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Functions from \texttt {model.py}}{1}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.1}Core functions:}{1}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.2}Creating regions}{2}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{4}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{7}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{7}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.2}Handling objects}{8}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{8}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{9}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{9}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{10}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.3}Get data}{10}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.4}Additional functions}{10}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{11}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{11}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{13}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{14}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{15}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{15}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{19}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Notes on other files}{20}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{20}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{20}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{20}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{20}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{21}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{21}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{21}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{21}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{21}} +\@writefile{toc}{\contentsline {section}{\numberline {3}Todo}{21}} diff --git a/doc/interface_functions.log b/doc/interface_functions.log new file mode 100644 index 00000000..5099a1e6 --- /dev/null +++ b/doc/interface_functions.log @@ -0,0 +1,846 @@ +This is pdfTeXk, Version 3.141592-1.40.3 (Web2C 7.5.6) (format=pdflatex 2010.5.19) 13 MAY 2011 12:08 +entering extended mode + %&-line parsing enabled. +**interface_functions.tex +(./interface_functions.tex +LaTeX2e <2005/12/01> +Babel and hyphenation patterns for english, usenglishmax, dumylang, noh +yphenation, loaded. +(/usr/share/texmf-texlive/tex/latex/base/article.cls +Document Class: article 2005/09/16 v1.4f Standard LaTeX document class +(/usr/share/texmf-texlive/tex/latex/base/size10.clo +File: size10.clo 2005/09/16 v1.4f Standard LaTeX file (size option) +) +\c@part=\count79 +\c@section=\count80 +\c@subsection=\count81 +\c@subsubsection=\count82 +\c@paragraph=\count83 +\c@subparagraph=\count84 +\c@figure=\count85 +\c@table=\count86 +\abovecaptionskip=\skip41 +\belowcaptionskip=\skip42 +\bibindent=\dimen102 +) +(/usr/share/texmf-texlive/tex/latex/base/inputenc.sty +Package: inputenc 2006/05/05 v1.1b Input encoding file +\inpenc@prehook=\toks14 +\inpenc@posthook=\toks15 + +(/usr/share/texmf-texlive/tex/latex/ucs/utf8x.def +File: utf8x.def 2004/10/17 UCS: Input encoding UTF-8 +)) +(/usr/share/texmf-texlive/tex/latex/ucs/ucs.sty +Package: ucs 2004/10/17 UCS: Unicode input support + +(/usr/share/texmf-texlive/tex/latex/ucs/data/uni-global.def +File: uni-global.def 2004/10/17 UCS: Unicode global data +) +\uc@secondtry=\count87 +\uc@combtoks=\toks16 +\uc@combtoksb=\toks17 +\uc@temptokena=\toks18 +) +(./interface_functions.aux) +\openout1 = `interface_functions.aux'. + +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 8. +LaTeX Font Info: ... okay on input line 8. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 8. +LaTeX Font Info: ... okay on input line 8. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 8. +LaTeX Font Info: ... okay on input line 8. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 8. +LaTeX Font Info: ... okay on input line 8. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 8. +LaTeX Font Info: ... okay on input line 8. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 8. +LaTeX Font Info: ... okay on input line 8. + (/usr/share/texmf-texlive/tex/latex/ucs/ucsencs.def +File: ucsencs.def 2003/11/29 Fixes to fontencodings LGR, T3 +) +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <12> on input line 10. +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <8> on input line 10. +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <6> on input line 10. + (./interface_functions.toc) +\tf@toc=\write3 +\openout3 = `interface_functions.toc'. + +LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <12> not available +(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 21. + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 46--46 +[] \OT1/cmtt/m/n/10 the size of one side of the simulation "worl +d". Units:[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 46--46 +[] \OT1/cmtt/m/n/10 The simulation "world" is always assumed to be a cub +e with[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 46--46 +[] \OT1/cmtt/m/n/10 *periodic boundary conditions*, with 1 corner at [0, + 0, 0] and[] + [] + +[1 + +{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] +Overfull \hbox (17.24684pt too wide) in paragraph at lines 76--76 +[] \OT1/cmtt/m/n/10 the radius for this Species in/on this Region or + Surface.[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 76--76 +[] \OT1/cmtt/m/n/10 the Region or Surface in/on which this Species c +an exist.[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 76--76 +[] \OT1/cmtt/m/n/10 Optional. If you do not specify a Structure the +Species is[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 76--76 +[] \OT1/cmtt/m/n/10 If a certain Species should be able to exist in the "wor +ld" as[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 76--76 +[] \OT1/cmtt/m/n/10 well as in/on one of the previously created Regions or S +urfaces,[] + [] + +[2] +Overfull \hbox (17.24684pt too wide) in paragraph at lines 164--164 +[] \OT1/cmtt/m/n/10 the vector [x, y, z] from the corner closest to [0, +0, 0], to[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 164--164 +[]\OT1/cmtt/m/n/10 """create_cylindrical_surface(id, corner, radius, orientatio +n, length)[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 164--164 +[] \OT1/cmtt/m/n/10 the point [x, y, z] on the axis of the cylinder clos +est to[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 164--164 +[] \OT1/cmtt/m/n/10 the unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] al +ong the[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 164--164 +[] \OT1/cmtt/m/n/10 the length of the cylinder. Should be equal to the w +orld_size.[] + [] + + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 164--164 +[]\OT1/cmtt/m/n/10 """create_planar_surface(id, corner, unit_x, unit_y, length_ +x, length_y)[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 164--164 +[] \OT1/cmtt/m/n/10 the point [x, y, z] on the plane closest to [0, 0, 0 +]. Units:[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 164--164 +[] \OT1/cmtt/m/n/10 a unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] alon +g the plane[] + [] + +[3] +Overfull \hbox (27.74675pt too wide) in paragraph at lines 164--164 +[] \OT1/cmtt/m/n/10 the length of the plane along the unit vector unit_x +. Should be[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 164--164 +[] \OT1/cmtt/m/n/10 the length of the plane along the unit vector unit_y +. Should be[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 180--180 +[] \OT1/cmtt/m/n/10 """Add a Structure (Region or Surface) to the Partic +leModel.[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 180--180 +[] \OT1/cmtt/m/n/10 a Region or Surface created with one of the fu +nctions[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 198--198 +[] \OT1/cmtt/m/n/10 """Set all 'other' possible ReactionRules to be repu +lsive.[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 198--198 +[] \OT1/cmtt/m/n/10 - a repulsive bimolecular reaction rule (k=0) fo +r each[] + [] + +[4] +Overfull \hbox (32.9967pt too wide) in paragraph at lines 222--222 +[] \OT1/cmtt/m/n/10 reaction rate. Units: per second. (Rough order o +f magnitude:[] + [] + + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 222--222 +[] \OT1/cmtt/m/n/10 There is no distinction between an intrinsic and an over +all reaction[] + [] + + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 241--241 +[] \OT1/cmtt/m/n/10 reaction rate. Units: per second. (Rough order o +f magnitude:[] + [] + + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 241--241 +[] \OT1/cmtt/m/n/10 There is no distinction between an intrinsic and an over +all reaction[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 269--269 +[] \OT1/cmtt/m/n/10 intrinsic reaction rate. Units: meters^3 per sec +ond. (Rough[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 269--269 +[] \OT1/cmtt/m/n/10 overall reaction rate (kon) to an intrinsic reaction rat +e (ka) with[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 269--269 +[] \OT1/cmtt/m/n/10 the function utils.k_a(kon, kD), but only for reaction r +ules in 3D.[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 269--269 +[] \OT1/cmtt/m/n/10 bimolecular reaction rule (ka=0) for each possible combi +nation of[] + [] + +[5] +Overfull \hbox (1.49698pt too wide) in paragraph at lines 269--269 +[] \OT1/cmtt/m/n/10 reactants for which no bimolecular reaction rule is spec +ified.[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 269--269 +[] \OT1/cmtt/m/n/10 You can explicitly add these reaction rules to the model + with the[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 302--302 +[]\OT1/cmtt/m/n/10 def create_binding_reaction_rule(reactant1, reactant2, produ +ct, ka):[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 302--302 +[] \OT1/cmtt/m/n/10 intrinsic reaction rate. Units: meters^3 per sec +ond. (Rough[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 302--302 +[] \OT1/cmtt/m/n/10 overall reaction rate (kon) to an intrinsic reaction rat +e (ka) with[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 302--302 +[] \OT1/cmtt/m/n/10 the function utils.k_a(kon, kD), but only for reaction r +ules in 3D.[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 302--302 +[] \OT1/cmtt/m/n/10 bimolecular reaction rule (ka=0) for each possible combi +nation of[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 302--302 +[] \OT1/cmtt/m/n/10 reactants for which no bimolecular reaction rule is spec +ified.[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 302--302 +[] \OT1/cmtt/m/n/10 You can explicitly add these reaction rules to the model + with the[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 332--332 +[]\OT1/cmtt/m/n/10 def create_unbinding_reaction_rule(reactant, product1, produ +ct2, kd):[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 332--332 +[] \OT1/cmtt/m/n/10 intrinsic reaction rate. Units: per second. (Rou +gh order of[] + [] + +[6] +Overfull \hbox (27.74675pt too wide) in paragraph at lines 332--332 +[] \OT1/cmtt/m/n/10 overall reaction rate (koff) for this reaction rule to a +n intrinsic[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 332--332 +[] \OT1/cmtt/m/n/10 reaction rate (kd) with the function utils.k_d(koff, kon +, kD) or[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 The world object keeps track of the positions of the par +ticles[] + [] + + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 a ParticleModel previously created with model.Pa +rticleModel.[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 the number of cells in the MatrixSpace along the + x, y and z[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 axis. Leave it to the default number if you don' +t know what[] + [] + + +Overfull \hbox (43.49661pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 The simulation cube "world" is divided into (matrix_size + x matrix_size[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 x matrix_size) cells. Together these cells form a Matrix +Space. The[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 MatrixSpace keeps track in which cell every particle and + protective[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 neighbours of particle, only objects in the same cell an +d the 26[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 (3x3x3 - 1) neighbouring cells (the simulation cube has +periodic[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 The matrix_size limits the size of the protective domain +s. If you[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 have fewer particles, you want a smaller matrix_size, su +ch that the[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 protective domains and thus the eGFRD timesteps can be l +arger. If[] + [] + +[7] +Overfull \hbox (17.24684pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 you have more particles, you want a larger matrix_size, +such that[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 378--378 +[] \OT1/cmtt/m/n/10 (N * 6) ** (1. / 3.) is used, where N is the average num +ber of[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 400--400 +[]\OT1/cmtt/m/n/10 def get_closest_surface_within_radius(world, pos, radius, ig +nore):[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 419--419 +[] \OT1/cmtt/m/n/10 """Add n particles of a certain Species to the specified + world.[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 419--419 +[] \OT1/cmtt/m/n/10 Make sure to first add the Species to the model with the + method[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 437--437 +[] \OT1/cmtt/m/n/10 """Place a particle of a certain Species at a specific p +osition in[] + [] + +[8] +Overfull \hbox (38.24666pt too wide) in paragraph at lines 437--437 +[] \OT1/cmtt/m/n/10 a position vector [x, y, z]. Units: [meters, met +ers, meters].[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 437--437 +[] \OT1/cmtt/m/n/10 Make sure to first add the Species to the model with the + method[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 462--462 +[] \OT1/cmtt/m/n/10 def __init__(self, world, rng=myrandom.rng, network_rule +s=None):[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 462--462 +[] \OT1/cmtt/m/n/10 a random number generator. By default myrand +om.rng is[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 462--462 +[] \OT1/cmtt/m/n/10 used, which uses Mersenne Twister from the G +SL library.[] + [] + + +Overfull \hbox (53.99652pt too wide) in paragraph at lines 462--462 +[] \OT1/cmtt/m/n/10 you don't need to use this, for backward com +patibility only.[] + [] + + +Overfull \hbox (41.35085pt too wide) in paragraph at lines 470--471 +\OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ +cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 493--493 +[] \OT1/cmtt/m/n/10 asynchronously. This method bursts all protective do +mains and[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 493--493 +[] \OT1/cmtt/m/n/10 the time at which to synchronize the particl +es. Usually[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 493--493 +[] \OT1/cmtt/m/n/10 you will want to use the current time of the + simulator:[] + [] + +[9] +Overfull \hbox (22.4968pt too wide) in paragraph at lines 493--493 +[] \OT1/cmtt/m/n/10 This method is called stop because it is usually cal +led at the[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 493--493 +[] \OT1/cmtt/m/n/10 end of a simulation. It is possible to call this met +hod at an[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 493--493 +[] \OT1/cmtt/m/n/10 earlier time. For example the Logger module does thi +s, because[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 493--493 +[] \OT1/cmtt/m/n/10 it needs to know the positions of the particles at e +ach log[] + [] + + +Overfull \hbox (41.35085pt too wide) in paragraph at lines 494--495 +\OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ +cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) + [] + + +Overfull \hbox (41.35085pt too wide) in paragraph at lines 505--506 +\OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ +cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 517--517 +[] \OT1/cmtt/m/n/10 This function resets the "records" of the simulator. Thi +s means[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 517--517 +[] \OT1/cmtt/m/n/10 the simulator time is reset, the step counter is reset, +events[] + [] + + +Overfull \hbox (41.35085pt too wide) in paragraph at lines 518--519 +\OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ +cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) + [] + +[10] [11] +Overfull \hbox (32.9967pt too wide) in paragraph at lines 626--626 +[] \OT1/cmtt/m/n/10 """ Return a generator (using "yield") to loop over (pid +, particle).[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 641--641 +[] \OT1/cmtt/m/n/10 a Species or the name of a Species. If none is s +pecified,[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 641--641 +[] \OT1/cmtt/m/n/10 all (particle identifier, particle)-pairs will b +e returned.[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 656--656 +[] \OT1/cmtt/m/n/10 a Species or the name of a Species. If none is s +pecified,[] + [] + +[12] +Overfull \hbox (27.74675pt too wide) in paragraph at lines 656--656 +[] \OT1/cmtt/m/n/10 all (particle identifier, particle)-pairs will b +e returned.[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 685--685 +[] \OT1/cmtt/m/n/10 (Species name, number of particles)-pairs will b +e returned.[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 701--701 +[] \OT1/cmtt/m/n/10 """Return a string containing the number of particles of + a certain[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 701--701 +[] \OT1/cmtt/m/n/10 a string of (Species name, number of particles)- +pairs will[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 713--713 +[] \OT1/cmtt/m/n/10 """Return an iterator over the protective domains in the + simulator.[] + [] + +[13] +Overfull \hbox (11.99689pt too wide) in paragraph at lines 742--742 +[] \OT1/cmtt/m/n/10 """Return three lists with all the reaction rules define +d in the[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 742--742 +[] \OT1/cmtt/m/n/10 - reaction rules between two reactants with a reacti +on rate[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 750--750 +[] \OT1/cmtt/m/n/10 ReactionRule.__str__ would be good, but we are actually +getting a[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 762--762 +[] \OT1/cmtt/m/n/10 """Return a formatted string containing all the reaction + rules[] + [] + +[14] +Overfull \hbox (17.24684pt too wide) in paragraph at lines 801--801 +[] \OT1/cmtt/m/n/10 """Return True if a and b are equal, subject to given to +lerances.[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 801--801 +[] \OT1/cmtt/m/n/10 relative tolerance multipied by this typical value, and +will be[] + [] + + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 801--801 +[] \OT1/cmtt/m/n/10 """Return True if a is greater than b, subject to given +tolerances.[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 801--801 +[] \OT1/cmtt/m/n/10 """Return True if a is less than b, subject to given tol +erances.[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 801--801 +[] \OT1/cmtt/m/n/10 """Return True if a is greater or equal than b, subject +to given[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 801--801 +[] \OT1/cmtt/m/n/10 """Return True if a is less than or equal than b, subjec +t to given[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 841--841 +[] \OT1/cmtt/m/n/10 """Convert a reaction rate from units 'per molar per sec +ond' to[] + [] + + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 841--841 +[] \OT1/cmtt/m/n/10 """Convert a reaction rate from units 'per micromolar pe +r second' to[] + [] + +[15] +Overfull \hbox (11.99689pt too wide) in paragraph at lines 841--841 +[] \OT1/cmtt/m/n/10 """Convert a concentration from units 'micromolar' to un +its 'per[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 """Calculate the 'pseudo-'reaction rate (kD) caused by d +iffusion.[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 kD is equal to 1 divided by the time it takes for two pa +rticles to[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 meet each other by diffusion. It is needed when converti +ng from[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 an intrinsic reaction rate to an overall reaction rates +or vice[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 the diffusion constant of particle A plus the di +ffusion[] + [] + + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 the radius of particle A plus the radius of part +icle B.[] + [] + + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (kon) for a binding/ +annihilation[] + [] + +[16] +Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 the overall reaction rate for the reaction rule. + Units:[] + [] + + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind +ing reaction[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 This one is a bit tricky. We consider reaction rules wit +h only 1[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 reactant. In case there is only 1 product also, no conve +rsion in[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 necessary. But when the reaction rule has 2 products, we + need to[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 the overall reaction rate for the unbinding reac +tion rule.[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 the overall reaction rate for the reverse reacti +on rule.[] + [] + +[17] +Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind +ing reaction[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 Similar to the function k_d(), but expects an intrinsic +rate (ka)[] + [] + + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 instead of an overall rate (kon) for the reversed reacti +on rule as[] + [] + + +Overfull \hbox (38.24666pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (ka) for a binding +/annihilation[] + [] + + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (kd) for an unbind +ing reaction[] + [] + +[18] +Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (kd) for an unbind +ing reaction[] + [] + + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 996--996 +[] \OT1/cmtt/m/n/10 Similar to the function k_off(), but expects an intrinsi +c rate[] + [] + +[19] +LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <10> not available +(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 1056. + + [20] +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1099--1099 +[] \OT1/cmtt/m/n/10 def __init__(self, logname='log', directory='data', comm +ent=''):[] + [] + +[21] (./interface_functions.aux) ) +Here is how much of TeX's memory you used: + 506 strings out of 95086 + 7733 string characters out of 1183255 + 66134 words of memory out of 1500000 + 3729 multiletter control sequences out of 10000+50000 + 8578 words of font info for 31 fonts, out of 1200000 for 2000 + 28 hyphenation exceptions out of 8191 + 23i,6n,20p,323b,243s stack positions out of 5000i,500n,6000p,200000b,5000s + +Output written on interface_functions.pdf (21 pages, 92315 bytes). +PDF statistics: + 102 PDF objects out of 1000 (max. 8388607) + 0 named destinations out of 1000 (max. 131072) + 1 words of extra memory for PDF output out of 10000 (max. 10000000) + diff --git a/doc/interface_functions.pdf b/doc/interface_functions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..23375729af9377b1c371b3e36a732439af741ba7 GIT binary patch literal 92315 zcmb@uWmH{T)~yW$2<{%--QC?GxJz(%cXtVH!QI_81b26LcMEZ8*kuMT-(s5-~|BPaG5kC)SS-_4hdGZi|$ z68{?fjIsuX^ov}|>IK_ml2(n;*^y)JhdT9hgYDdfP&MjJ%AJY&Gt&ZlBUIRNv?PRYi(hKD=(_jy;rlpP9nJQ;Yc<_`&Lc}SIKFpPNc}52J3?CRcMmsO_(s|sRlCNNS>5TMfaI)kwj44filp+-2x37S|p85WBzb{Lqp zZ!~qmR(r9O-I>_I+(NaZydfyU6h``MlTMu-(+ku(P|{WR7KObeB1-F?Ys& zKNY!CX!QNGASeC6VSzluxV~T%>5tq=;oT-HJS+VnN!CR$9so?)#ZxhoufZ|^I>N5k z*#6^_v{u9C_{iWF(f4@01hu_}Xt& zasHu*)l27PR@CPTb_SRVi@Td<(uJR>)~M%O*K6l7-1pH)cMSs+?V#)8+`e~%u4Eq% zq6&g=u$YuZ2+9Q3K)-K!o@#C`1-0;GN#hNc=wcJLrX)=Iu@I5jc_wc^cyW#kgFpq} z5{|yJpo%L1_c2w&p()Y4d)L)Lfe6^nC7xrSbC_AN(F8w^n_J$}%Ts@QGSVMjFpsOt z{v_{Q5IOtED9Td?%a>`U^-QVxVd)n1J983Jl+*g-#%+crcAl5CfG8r&Sq9)0&@>Gi zY~p(Be%g?-kIi>ny$|Q)WJ9*!pM00UXwO=`fZ!?6gh3cs>i@~Ox2rckLeT%jNd*^c z16&#z9U}t>8UZUydjm^*JKR4&DhEL$XJBXLV5?_fhfDWAFGv~ao9OUcIpeC+zP-T2 z#zale!i>wxNJq`erU~)(F1ByWaq0f&uerH#|GeKHzyHN$;kSFd0s4)?bWFI6f4uD< zm&~|Ke_qlv<1+tw$%@PJ*Nd#Utbbjy;j;aC$v}%s_s8du!+oPTjf}08p1grQuKL@5 zBPfhZqhR1{kE{6x`2YJU{q~V$GqmtPgIXVs<=3Q|=G0xJ*Qs8LXdG>qua4j~F2xOBuL zKjm`|qpwePHi`?K^!E?qdpj3n#be`z>7lTLYH+RH`k74G+h(fU5i9n@X);AV2UxYB zfmAnmH(p1g%ual$5_G&l%^@~@eoQCD-=4dcMTh`iGc-1^|3W*5Y-j*4&O4AkTlSr7A0eK=}xQDt*p8 z$&!8l^t}m9bFK!nEMcgf7e%76OdIyDrZx$vjdBRob=O1BE2U>yVckO)WdOgEuh9RU4_&!h=*gjpi1UTDxtX)H|B&~i{)DDTbd@%A=AS%x?8~) zpcd_ByL*^W+q|dubL-Dh4|i^#AXLAzAh7zy+qjf%XawvXxY#u-!btT!yz?chKSN(Y z9Mbl@>Eg6RD(Z8RiAp~bM!--Lk3>Q_r|R@U+GZxfk%L@f_^7&8BO}v@c!ASi%4B{+{-Ft$r;$vA7JoTdtfb5& zMOewF`!2j+6&W%cGCH77YR`E&VpT4#m3!Vdm$Y5s@mnk$aW#s9)2mg^1-xdxH=$6oxNxIRpNM9N@G+jFQnJ)rNyr--Lg{ku)?PL@_!&Kd$;T!~E z&hF)D3FXdh<+I7XdeZd7ZRy}4VeWREnq$7KiO z%-R#CFmbY;UPJ7>o4+PR9_|Ept@?Up!&;?@^7D-eoJdTjj9jTP+%EW*U^N^GU&;Vj z5E2L=ybjzxZpO`}Fq-U36!|hZ z&0dEisRV31K~*AQ8flJARg5tRb6@!tfW)`2z+AoeID#qsCN@!iQUH4Lmst*I=yRpiFO{(o_Juj;avg+}D69CtL2E5S~MP)H|3`gJ}rA zUV+$os+d@r^qja8bI1#6EkD+Bh3i_svcl>za2tEAli4e)(~II8#sW;vf)L1Rmio7Ar$+mzorf6_EDCY)0a$1~oUh09_H^$sl0gDY92Slmvc zM=mg_JDQsG-Yll?GCTPyIf@EtT8!2+VKBHFy2uaV^;%Xh&z4o%a8IO>o1?Dg@Wf^Uf=pCxCxjE!&q6pnx$*<)SvHM%^Sp35e@hjTGDhk|;j_ zC|iBsfx)a6+}fO`v;}>BDUN>)9*B|gp<>{~^it2abM>w=2o?O>xHDfWKBg`W4GHY$ zcIB_caNfR*7q@992k>#ec58^MwwHU`!fRjN(TUN1vjI*=kU`e9BkS%@CganOehFuo z0*huvPbXU_M-Jr~3#Wuf;6+D=u>G3j#$Pc}UnBt$0cjS5UI`^6#1HpcPuJVpq|jSC zK3JEu%l%qx(Ax)fbaLoOzVR@xjkt-h?r+2K(tWJ+j+fFGQyilsw^r&4t(ZJ^nE zx4P!ch{{L1uKNU%%E@=(Zm#ZrxFh9kM5Mx)-c^Xh*ed-j9hKe7{2Um&!s)|u|5Z4p zOs*_2AcWW_Pl&Yi7f2d;i?(0j^^cnAFE;%WuRrMZ&o$qVlIy?6>mSSiy5+ydD~&My ze`Ja1e>Ql3l9ri;;kPZ(NkXUXA}w6tnF|DL0bxXdLFGG4_9@92fsa`Huxa%={%zi% z1at{Aj8@v{WtfuYG2@u7`(n4prcSRjp4An^Br8i=>(B$VFnU(>cl+xxf;PvRpmbRq z<6m-D@8lhKMRe?48+`oz@Qrv#GPro4`e}31Ka!yfM>|}XZJ8cTY)8G5fS?Yz8Bzu8 z=JrHxl_7X+^;za770nf$*b;#Q(JMlgcqG4?ZW?IPoHN(S-bWH%dIHRkv@nJ6G=6R# zEDxxq2pIenBQrf$pg(?wp2kI=%iOIGuHsDQ7xJy0E?Sq@Xl}A^M9wp%B(~;0FH6#QwDdSG@y6zr;p$sPg7)jTR(zXU^ z`f-t+<2|Hpf&oVw=@yfHE2q{Glas?1p-JZ)y`P{F6trGA!>npf@kfgfcf^C{B4xCD zKnheRr~~maz8plmNeD8~wQs$X?b*oWnrSNn<%PSAmQf8+ru@Iq( z3bASAC^1U*epxeb8vvl=9e5FG0llHu)V>VR@*`a+0t$+dQDRt9`sy|Kite4A2(T!< z{H1?k;o`I(vkMB*m&T=+lFnj(JnZ2-Fp|g-QxfOc)&Z_eZy?*tM`;<2&Yed^)vU11 z#sj(4j1*V-u5B)BnRYR#Q}~kyM$SR|9i|b1Q{Q5*{ZfHC)`gAm>gqL{R@JQ_O!3tr z*r+zEpOqYekY$BQW0Q0-Pjs&q%PNcFe!-NQm~4I3>VAUy$tTQPq+K7>h<7p$j^$1? z3YzDV(KB&6T#?NUZv?RC%*38ZpxLZl=EsKh{K>7HHaKh3q!CX+}TFK@l@AfFK4*qq=ghXF%_~EwkZvd zSNhQFOS@GXH;wjq7-$K))+p}!Afek<@}Wq}0&P#`%DNn6f%J{w{-$akPxyLnKM=t3DJY<1H;&89Pi|Uh~u-fIc<@4NW=Dm z+{<;Tn=Jm=0VM<6bGaMPXx+Bjd&&}pV&VVXvTjNFKfwp;Xi+nbEnH?6hRnN)Ocm)$xn=g{@ib6yg*&FkoJhmQ#HC1oNtWqz4I^Feo?}L#xW9>_WOQVIv|bS}prh25!|7z{B6;>=vbI-^4y% z9Q$6wa3@j9^`Cr_8+0VE%FdXK>VrC!o|bOO0mTvd2(W#Tt6<#NV=yEoz$*hWjxeP+ zg{7qx&9U*T!qu3^*1o3>$(ye3CtXfc zJ1>*$*EDUIXKfbrVufDRsZ$&`vXaP3FRaP8C7*-VMKg!H^H zF4IC`pvH(dGPypL#LAOO+vS5n277Z({b?IZVz53iYB60d?pGszR`p>^9BU5vC}nMR zH7gvP%at>(C+5pcTUy)Zh0t&X=BZ)+Ky(=462QG1KKNv2$pG3bGbEB9ebJV zi#-B_ThAJ+gQf=foKbRR2&Jm0E#(~d;Mtp0_)E(WaPjOB7i}UyBa6uAk=lMn5MM)p zI#Cbkf7_99wzGPMKaJy*xYOtkh_>5Ig?0!}h_6wkk$NQcw2My^i_&a@b8$g%_KUzt zXN-4-p$9S<-NbR?l=9~A)Z2f*_~8D1NZ}T4s)aY;eR4xEthJgtDil~SqldIZUry}8 z*^$B$hugPW&f?7@6?lXGqR3SSlgHxsVn(mogImGikK%V)W#aKD$F&mucc`~71IFxa zN7HH!(sQaDnfl$Arf)m(^5sgtZzL;{nIP>k&oZE=RDNAkvPm6ES zwcO{hssDbx;DF!^*2%yFktRWq!p6Pj305$D-}qhc!f?|3nN$~B+ppL=K<6!(?6w71 zF~=(J2-?8~2-Bji)*$hSp%^oW-h(HkG1tBUEF=XBjHg)eX-gHToBhg%V(Frw)PJRX zOT!Q%sR~tzSjJ+qst)zS9u z^U0%Dm%Q!D5`ZKtG#6-Wpk1Y#6C%i*%Jx?YuXjutU!a%*Jftv9@ipvxFDl1k-DE`_ zJ^2p`wXq4?w(+jq9MLPNDwdBLh~IlY%FsQ|z92a}6#|H(w^a6^TZ?_MjKhme_7?u$ zNHL9iAKJo1D#hLcd>a+e?738kZXYO2aW$4nAWMT8A-1 zDhux6eh;5U5y~V(ulKl-rW53bH*HLxKl(Cva7jtb;^T|^-3HdEM7&~AVOp|9(|y)p zZn;wqinP`KC;|g*&xf!uHJaY*c51Xsb#795WX639$Jh&%Y9eN5oW$IwYig2k=LB#n z@8-c%x1&@C;d~Q5JxAd0kHYtT4CcJG-Duw$kzEBXxg+XpX==ng6hKa~JH+@x6n3mn z8=A9l1>+Os&yZbT1tBGMuVDqHEvSP}WaI3#qS6dQnIMygcV1ORIZ*JNkx#UgrSdeg zoVvwsG7-cXDe3Z;Bp+OAXh-+#w(k{+mqb$}@f+3cqP%rmYzFwHC!rx#nfIaF>|;~5 zN>LHDhVR~OPVE$K>KS31A=dX7=zKtfFH}Bd;GW;a*`ylew7yv6WA-JBkb90fRc!l>= z0{Z}b1~&Py$UKPs;jm%I>Z9Aw4Rv+IlfpDe!Q)J|H2+)EivZ?sOUTiU?J6 zd}q5v#SMNZY8bg?hi35hj*TJeHebK5>&cZ4jJdVN@_7CO4myt!YDm{V&>tzhzDteo;`e(fxkLUZ%8W z^_H=Fo@vA1kfouJ@QRA9v0JRbnkdc)Ep{;rQ8RacnD1wnXzB0-%>d8WpU5YZs~lOl zTX9-TbE#wdvL+ZdfKjHrVOl*v+(|E-sKeN#Q7QvhPV#~Np1v%1oN?xuq%EmOhm$C~ zJDh@}Gj-l)b!IT)YgBt%lNYpEX|DJbgrwZ?$vU7UB&8jBbXw~eb-M+(BucC*T&4r2 zI@-tV`UGl6TTS)fo zOy^-ls8Nd^5o1h_O>J1RbKL^pS{>v9GRZ3?102t1Y>2m`U^T^08v9ySY!E2FNV|X! zl1{!+J78;RzZB&<1G;g3!$zC6EUziCss;>Ms{u`EC?hsoB5!r%8m>S~B55)#|4A+k zF=#k-uU&>`iVxl{i$nE$OzAKmwdqNN^klZRY$5dwSna&Lw)dv7iyF%k*Az!@0|KH_$rgF_ z*{hJ8aslH`&D^(x$LoV8eZB?6O3BD%ctCqc)~_pd@jA{YWlnWW$uOo3n+ zT}*hI>c(y{su}ej2gcf>nRNP0EU5YGrcj=V9&NF@9ZXH9?5ngZp-u2S$r|raCEqJ& zf24|26VL0VuE-!yp*PwO=8O}(V%CepwCET|D>u+ej5C|*LXV^fkIu%^3&O?|Vgi&cm(cwdTAGvj-;kf?t_^XURn`fvzt7Ro-75a3e&!?ca~Bh*v4_Bg zve48qrc_^m6+5<>oQvG6lhB-6Wrkk|CBU&4t?!dOjcqHX6#5mLlH<89eTk!5$Gl1f#D|f%FTotU$}y`_((jAT0&( zNz{&%6E+jpY3&_xAOwYOOF%PvAc#R51saWFUOLjbI^i@*i%-sfPU~5`h(Qn8oKJ3| zyLg&b-5%XalFpCDiP+;vu*o0RCs}|*ts{pqOcxVo8XD0byO|BYPDt)-E~g(6VscS? z+HM@3z7l=wIe3xTD#6)*R0}IQi(vBsAi`0;e=*@|b>MHLk(__E+b6H0~U zMFKe}PwQR5bQq{N)|QtJn~@o!Thlgg>rQn@u}YP2%1HpFE#{EvRTihH+D+!pxg$p7-CP5c)%Kw)3e~XKDwCkn-EEX)q?*WjT;a zIx@vNv_E1Xlw~S(v#QqPl^;QPyGqGHJq4?3mZL!o1}aKobT8gh72_i%&4OSTX>-+l zn`yc>>oK3WQ%~O*&_ND)0GRA5hVW52ZklBNTt5=X>yh$m4ho_zMh?5J(a$3oQ7IoM z(Q_S5&|}U9T%BYB)Vp<44Ta_zE%n~bS3|+7cEyCDo*x2tQLt&|vaStTiP3FU zK17{f_Ox)9Ld!a021(IoU!tAI9BOG>e#wb@dyhz@45nvsfw^O`*r|fD#G(R1-FBAo zfW_*Sd|6DJza^|VQ6+Zh8CY;i$^7u~#Tg_+e;6XkZ7aeQ#Sf)tw*WZ=JP9p}_NXig zsMvEJu84HlbSP3D>((!7_**~uYxKFfw`Zl1eFNGvmudO?xr|~9|Y*x0t zLQ=kWLvHCPj7T`DQ4j)W3t;4@0@`4FWdq{9K$z=3-4~xwW@F(|SWpTL^{R6c^!XTx zwHj0?FikFK2f)BC(zC|lVC?efxNr7dp(gz#wg{ox|9sx-hT7`PpCN@G*>HthOf~>@ zeA_QE+T;o&UHkC6v7B+E+jqzsayhxxJbBIFqVM@NY(Hg3mft*SRde$qZ2 ziaoPMPRaA#(>7%-Cz@p^T`Dscc+$Zsf`roE!BIn$jBL5}AuoXM07WByLD7GkHZc4x zZD9D1(uTiN^yitaUpEo{jsN>k+4`$Ef{yjKo47m5D{n($P+nu&I@<;?*0NLs8Sie# zmt1=Sb>|mb>w5T%C_?D^1_|#cAJ0ToV-Pt``vRA`DoKWrgyaal2pw8fTN`s^Wis)J zOez;Z2?u3UjrdUVz+&;|6*?c!#q3KQ_+!o$SR&M`l$PRAZkNbT!Q2{1Imqt6D&b(N znv=K}bK65t@q2bWG~N>h`!e+wiXxOe%^bBf@U&e~isq!7VClv65vm}DLWENt8ER8d zTz-gn*|_qITw2LsSrHI3I?gdpz;%7DefGt9+Ug9T#4vIwrN5rDaUM4^8=+J%hw-4IC1nG=w?QBf zw~o>$8??cU3+j3NEGyGV%nC4p4=c@egqI06g{dTUZh7&zd0ULzEf%<$yDyYC?^3gq zv0G~h5}N&_TDuFp!HQ*eBGIo{D(Vlx-|7S>2(C_CsD%ojry41m!Y_&#hg!5l9E@M> zD=3yd?jI6^C&~C(b0izi7V+_&*-?(4RYl(3LbJJyT{J_!j%YU~wH1@nvH@fF$!g=p zDkg77pdM z8$F9>v9S@JrPtY(cpecpPfk2Ok9dwp_D!+rL5+o`JwBpH4!Be&0HVY^Lfg38`4A>nB6 z2&ImT$irc<*T7#^OZ+3!YGcif7?TAp5}Y$efp!1U?RPcpa;KG9L4)qf)8 zxU4w3QUrC~a71nnbeenFa%g-#?qSZs&ulyF1jyxYBzRv)iJKH zeuB<%t2-DFntC0-a!H4NP5T6aTV+ICPaB~Lc<9AU9Hx7T7iLw6v1bvT^=37jLtdR> zsfz%naM+bkEdMrgl+a+o&&WEBpiOV6`11U<9St+tU|}zouATetd-@jSSx(Q1<&Fv- zgvVS%wqJlB^ridSA}jYdOJkv;n|pGy3X8Fu@_HP{kFaC4##jEzP?}y5+-kQNIiK39 z&7A`HDYp`b@-*zQkebnim8tW$aVIvz&l`&d(qo8s`%=1T`Whu6E_*Ig15sG$p;p&9 z0NHSL!}-TPOofb-d{YP2xTo+5f>GsGjAD&`mzxmFGN@fQ!==NSwbRd+>$IL~J?=zl z1}S%G$UizWun-lFQu8#jGecqQt&l58x45B@i*Quy&Qqk6Mk{^iQlLztC?Ky%cE2f? zmAQ&e29CIgGKJk_VVpd0a49O5PVMF_Qx~JBrN~tH7$JE(%Lt$QRD zSezO1{`*IjpD3KTNkcMP`Nm_Va(MAh7{Vcc2dejjDXrG zFf3az6j#It3kTqnIV7ni!rXlCvsa5)N?5-4GDrI8!VyUmpnp-CTw?XRZHxOgA9;CSHEEiF3&RphL+>|xh zW@7FX7rbz1o?aL3J~z(s9IY;C(mu=D&Vt?1`;&q(+cG#lHuf;9xxY6u_Zt3%@O~~4 zel5H|9@~Ew-k;MF|7AYS_;XzR>#F3hUF^349mbzG;y-o8{1#^aZlE)x)EW7vtJXaV z8R-xtOsFK@JZ!ZS;zevV6P5;x08!c^qRQwK^O=r{5GkOdB46gjbg1tOSF2ypx5kzR zY_XiBfnxeUr&?@$B?XcVPK`$e+$R}jcG5CBT^8a_iN=%{gYdn7>bniwa>KpI+TsU8 z@VD2`C(t%+B~jT&?Y$xO(tYNw8RV?u|NLyg+rgDAHdqKwVkk1vUBr3WLk53F9#lQ8 zv&OYOgPy3by?F$I@*u!b#-F8b((9^+T)|ScIgRfr)~qW-#Hg+Zh345>E*dnuqQtd=M|FRD=t;KLXB@3K0lrF z7waX9_40KTt$^%tU3GG0V3!6Yt5NDlZrFMcatEPKcsv#7a(*?k@10p*No-EX6Em_4 za}?~jXh4jxB*s7tuqdvIUFBhy6#K9vPHF1{`lLu(=6Zz2>A4A?xH>`sQLksP3$!$lCJ53fG4-S95i&bpo!Oez1Xh$cjJp zIsve+d6&(>@%5JD<;zw=!K3R}U#>;?p}OWP4NC!3FWkydeyl@3xbJoWeyt0j6tVAZ zG8q+UwG3hkL(p>`^f-1)<=BxhGM!dTFC+NiND^?uRA(5$aUjwu*diDb(L=gh2?|-< zJ#b3cVx4IQzPMOTVHv=ba3mZxx-~#Wi`O5x0c|?xp?=K-Pvx{E@dAj^oICo341YeR z@M{_V@s$6w4F8M^eouys|6@k~(I#U2IS2i<4FB7RmFX8|l8*N6V9Af4e}7hNMrq~8 zQQ|*h05MQ};pv8lSxbioz;}VuR!6wwC>Cb%f}gW7wma6ac#w*sWApbV00p>X`dZdJ zE>|roELmhL$wi|B4@})uCK!oKQcJ6Gi5MxBmP~IN_9~tUB{@Ag`->}lyrDQ zqsraI#i9uVKRX0oJO_=eRNx#r8Ydyh)jZM>xnPdmS5#DtWFeve$qSC?^uP+MG1cx> zBT$vJyvRoZ!+?%oj|frXK1P=Csfog%8|A!-=MbC4~}G? zVmHaUe7eaOwmKUoK4lCS3Uz5<;GOUoWj%L<7HJxFM!FW&;-&s~fks-iqGUm#ggDe23OI((WW5$(?5iDa=Cp(@GFm9T>n{5Atb z$DrNSa1vW`nl1*HF+c20F<^fQN5v37Um3$hwoQ z#jwcC5YPv@;c9q7Y^1x1wWdk`M#k!(!%v#fFA3Z^Aht*IRqg<5%7u)f^(YZiVY%F zokrUVkOW|z4Xsl4ZPh#dd=~Zr--=yHxYW#%^P=H9lTAUq5brf&G;ViR?E2#?j9Tj& z-Cfu|*Y4HIKDJA2DwNBs2-U;hF!=(TG!#XgkL)YMot~V9WngH?#qY0UiWo3@2(#Fb zKJs01GBg^88!4YPD8IfFe6d;hI}(}xZAtJ0GyjA{rvIoU_!|;`EdSRf!C%hg|F3iY z*0^H&VUK?AR4vob5dPDFI9isUcuk5T9BTTJRf- z#6c;|^X=|V>Ck3v9u974D?TVCVy#2WyVS7^o(#Q=tBcM7Wc2z#<9UL%S?ASnxnZDq zCO2K!-*z)DN^hueNrf9%I$2@-%+!`^;!rrVvR6VtS8$_Eq><<7r%rIT#z>T98$r?P z*`2+!0eMH}TUSEFFozeq?r32Ssu&7rQHGZBGjyyZI*~>Y9GPDpb}k+5?QBfGor|={ zh4~wcR;mKz=q%eoVq#;jR%a_DhvAgOG||97%A-}ImqOJ8@!ZPD0hS!uK7N~FLe1FjG(<`VM{oM(&l2elsY7FNo?N07xsz+{bT^_tzQs>d+v6{S~;teNen^p(l&lUmJ;!Kjwv@^|J z7p9%Fq9ma}fqpZA+gp+l%qwA!SK}z?8}O_ne$@<*aeT`+4jM6B-jcPV^5MNOzB#rh z#}xFgK>`x8puwOdWty3;5}sdXCZ`?Zw2QIb4CHRUKi?KQ+O2FkT%Pz+E>0W}J_11^ zU&_K>z64mSsA6kp`F9n+cOc}oM5JH*XAIIIue!5H*xw-QOmT0UPdpdC^dwWf6#SO9O@Caq^XUG6*q?a|^!^dE{DqspTHn8M8n#SH%I@vx$6Lxr zdjL4u`%N1yRe%GJkl{qqF1x7~I%gi=xRQu`DC+4^JV8H-4sT%FcbjQ(z;$O_Fd-(=5;M(B#cXdTv-$0&7FDR|^^{WFKPT?!vD z^J>q++QH|@GvE0;_Q*XSXIdGNj~_o)kg5{&#=kF2@Q(q8AI0s9Bb`c}&^QtIb;+t< zAs1BUvTlCWB9!lObeI(9+IsU2&dou37CntzpjDqenY?t-Qz@+bsHxlx z-O4Bt!E?t5Nh`yzG$0X%)CztOHTIz=&aIOe2FD8K6A0sdD1hTle9#_<{jRNq zIo`fN#oL(`Nhb&w#vNH{|8f}TX7*BKUyv7J8HA6tiNP_YjLs!)Hjyb-3s`a452INyxmlJA73%N|PT)_~WWPQM=8y8fa zFF(jrk)b2vGvCvIuS7X{lKFpqsYO7&K146AG>B{agkF!}a^IrE<~Ji*u)U01W(E70 za+M7h9=J=4LC&K^`08xS#dQx`3w&|$3lH>jwDD{D{H@2p^dI#Y{!X8tE75<@=f6hI z+gba+A%}(S_lE=Pl>c%#f~{W}H;E4dKutvBd~3Omb&hI-G^4~YO^NC#mQ**^#)?jI zrLln2T?$iKZ`sz^_BawpQAGTfbk=)t(*+T&qFpwOp+{sfwm~DeVEJraM?Kzi ztB&N&8fwnkj$oIUaA%ra)YGAGTD=VITRrXhBYUNd#(oi^UhZ7R5nH6t~w4_B|$1TyExE+r2x^ zOD@Q({)lX{+-okRE09E_oYZAO0y5cXb_K*{x7-em}OLbZL#4=>1B3D>2B z98s4oEq1Mka{lt}8s8__P=nziTQE7{HNzq|KRSaf4v3htW>3Sokaj16a)UP(jz5}Z+ zHUdFEw(c~05nN6^hUXy3gC#Fm9GL7-q7@ot$nkbjC;j+ZSJYqWDmp2&?~8c1j56Q& zixxvlLP&^aXAulX7Lzf0KXFFY)DJ2;^8*@R zek!C99|En28wLfU>$F5D+9V-({K8uC6`6%t{P$VSX^N6>n!g{-Lv9qT+`V|H-H@|L@ABAIpEQT>7mbm-%O} z^`|>wq5b_J_e6Qf_ASU=d89ydf-i`0S%8a8rcP;AwVx0YQ1)=vC)9;jarPID?Wo{| z^@DdzOLm;1rtiM=ShI^fx8SrrH7rM7klhbbN>$TBTioEREMFPFed-U*x^bXyO9l0$ z9Y{!A+xnbvwitb;DnC|}gA0dZ?&m+!Q!a>1nU9L(tOt?AD9WpdPx_+G6IO5= z$SPGL=Z0(&JASge2smgRlOu=Lh6u~6Kd!FMc?QoOoUW9sC|9rMY4}xA#->*!%5o5L_puWxfY`E@KJLf@u}K zghMmBm5^(NX|X>`5e$2uyOrd_9v&EhG|BBuf1!1V>wNdIaT5&>wnQ>eR37{mt7ZKcZZ>|d=sOS>!8o$QL9r`>sJ-4cv-ZMVDS;K}Vpe2TJs_8e z_tBCk$`ID2`=FivvXbJdPr__6QTZK@Zmm0+`72OyFeO7=6P9`-r)(cEso7G-%rK|>+52+rmN4eP$bFTUC1&7A)`S^up+ z!~7rhXa42@ek}igDeIq2%%8GmWBw0ht;7}kHgJ1#rfgiEjSwaRmrLqOEah$!o!l5a z(}NBz=!+nXWDUs6Hdt}D8HRwA?xEahmj@E;@#uKr_!7-tWpNY>A-Dwy?i$!^ z+=4s7U4mPOXXdU|?#&2;zl z%&awQR=U=dBhsNYOTm~+0>Eh3!atay$c9UOQYbiIx7xzUlWg~c{HahvoEYnoFk{;S z^&aeEYO;`&-?{#ryr+h!k*Ij_-TJWnVc#8w$H(MjzF&`;kKFx+aDJop@9`kp#XKhq ziJCZt9^Io9@=&bd?VP?^M`{HeUh92qdY6@@@%rI49rq~{eUG&o$vW2j(vma^C-UZ1 z$mLRmv}yt#lqcF%E4HLdaB;)&m*jyotR-_q#2f_5^+c*{ z6BJ*VWs#eQR|;u_{vQj$UOz_S@y#-XrCr+P1I-K&(!ft+=Mi1T>C7u{T$OU31~nDNmgHY|XiP)>;r4J)%p(m_E9(+e7ZT957d`uefV!HKE1z%H$kE7VEt|} zkqi<^m9{Z3MmFgSC}@mYWZ&ZxnQ&;6yh4Q&SmyFr4Qn8*T}5A+F*AOZ&A7wZZ+JzS z{rcRPXviXtSL@^1Ml`xQG94~;@cc+AbXA~#I_qBbBAO?!9rlinq@kyE#`K5`fs3xK zpKAfN{%9OV2ICC&Sdx8_UZeUAmB1^bW;#UI9gC8XfU;a4>qy)g3ro}A+-BxYKh)!U zbv?ZGG|8}kP?PQAER8+1B`!Y+3r)4Zuqa3x)E{5-+d2%@#Mb)(g3lQzTj^7>?u} z*M(IzQI6H;a~T2B9kVR=vJ-0t_3sk4n`qSfe=B{1ff{Ht`zkiE+dtASiM%)*deg1& zC5bhyI(JcNLyWQ@of3eO1(zE^BuNB0C&PU8-LvFe(>4_A@Ltr_G3qxzVvYSU z8ag+O2Hm%HL~F#V`{ffnQr`%s5_zGdsMVuQ$qJhCF6*gIl(Nh34xlYyksh4y4-aZ$ z8)jj`$=rPK=21AI_zRo6rP_)L%WoQ66#DkGPR7Ug@nsKI^DkRdy_<+;kv2uKj?WN3 zLr|aY=)<1HhBPZKq!{FRtS8@QA{@bKNez7asFZsG?WUq(`*prQS|OI@mcaEob!W#@ zMU&Iqv?#H9kS_1KuBy8yp(+k;a#Hxa+t1aIPYqR;sw|!A@zO(S;Df5DP!LsKvLqPY zO0Rsr>>_M}-8T9E{$$xmX|>bWj9|x#(9$pu&`BXCC$Z+^#b>lckFd6B7)isVp9*bl zIOWhUtQ&+v1M}x9nlhS3-W;qhno6HX zNcIpJ+9Yli_e|j|L2`)aoqw$NXk8^dK%3!iBR-@BJ6@yT`y~Ghg@j-d2ZeKiq1kqX zepyObtFJzu8;*9!;jA7y;%9d(yqvUKi<|oc+!!B$sL9xQpML{Nh#wMU7Dtu_{U`0A z%un2M`-qqVUH+(M;c2{*7hKS zHMnKrHwXWT3BP#dgS6c5&yKn=COCF8-=nlGMV+!aF5-48h=y&p$QHAaXAw&^xPAgF z@I5{T=e>K~Oye+=odIg|8_D;u`&r@<=9wqXO<#P3(s@Sartk$2DzZwTH~_0PHxOT` z4J06#r=iM9tH3)hf)-irv|Y*h4oMdk$-|-GqWCNY%6<83P=7kakq4ZuUI}N%LzLsQ zvy_CLOGy#0{0eVZ9}G`U^2)aMjyRPnmR#c50-8)9jc(J9s{W=((A=y$Bl#`7=-1EF z_f^I0bWhHJP&vq^C_+}Cfm<^_2FiZO{CzwQT2)7Ho{X~w+h+ZowF8wb)4c7?n=3qO z&g<4%AH?uw+@854_=TBnb4J3Qp#rMUrXS6FaQ$%jk?uabaN^-PsiTUk`I*TlQK2V8 zFul=DiZpWvzMD^a2V_#)5tzRUndiNIcaF$xC3FSJ9L|{T#pzgkiJdol&8ykNPxlSt zc#)6huJaxLf=vGkYb%m(gDZ+9H6VB|RC-PJzL%GJ-_Y!xYXcJ#HR&RPYJxP_><+17 zEw8w+tH1IzjqoLm*(8ygo;8cNH8G=x5!Qll4o)%dunFwmn%MmPZ+N5X%~m8j7Nsc| z8hrovVavn_s2Em5EM6hDNLmg^kB~nZ`ToVEk@bIF3qD7y|F#xn{g2jy{}frDi~lc? z^>445{^P)+|M31b>%Ukb0+~S1-2Tmt1p~^ePlwQYT$Ui8lrxkXI&Hl#rTm_gNK7eL zd9IHAUOz~aab#tyXY#R`5m&t0%;+=yR2PwIU^nsm>yy*wt^4_4WzTRTNc7@fm zhxu`dge!N$9g8|S>LbQG`3EH)l*s6xaoJzpKjBE8oE0%uIm~_Er)<{n=1n(Yx2q@I zF?p?Cv^AV?6B)l=T){T(Z27%jZ~Yb?{h&y{CyI%q9mO3dY%f|ky%Vt8x%mZ*<=RVPTY@7o z_y(s}KjumZe+x>Zs2_ecM=h&@EK}wD7MWU=ip2p}n0k~;1g+|;t;~mY)iQMx6Kc19 z*-)5TD;1vCZE4X_JnjE;mc=~wyrx#v%3Fuf0v&igZ)P^eIN){Tv{%Ei`+b(S(B^8W zzi_u$A$l2ncpGNAq~x$;sb>l9Cx0(`syJFE6w(qiOG~Q{+<;t{YASz?g*!exshVkY zHja?D-dj?}9^hcCmm#4ZGm<0b0dTQ{L3SN7Kpql-A=U#DxmqynTcigQ6DkJ<^Vw}W zMLoSU(p$OV{LgB+CIT$)7s7XT3kih5afvI!ZwBsDOG=WZmAF}owjJL(!8Lg|B9L2g zXb5^tekR_@p@7&u*OsWv4Hn3GFw)|O&giK9Io9b(y>Y=?0IMtz20RRuH}08>GWHS` z0PPys!PKr@J?^T80Yj6Z{XlE1GUFFDZ$Lv|UpV@+yeeWqn&R9uBV3-k6+>LOzd8Y; zkeX(ghK%g1R&-`mGGmN;`G|d{yQ|ZcfnAIVxB#j2SKD0_)i$)TVBca8StW z_~=aDO;t%KzE#qCZd3EYVN9Gu0|PPe4nPWZzePJ^Re-as2Dnj_bo;nn5mnP+VC6do zb`_bR9(rvE4oV^X&`ptE(^~*@?6Om^GCB1}39PH`lxurdva??@_^z!RU|QMJN_viO zU~?A-p&TwF<}e`X_n$o;^r_vE%rDz0JH;P3C-e2Cd4vg0WP z(rYTuSK7vNJe^uYlj&+|e)^}G_UP46e}uzMys&bV*LQbVn_g&Ux?gZta=0-@$i>2~ zWUE+{;R22%^vO2m6;rZJ(Ubks=&n4*y#(%EZ=iAf=(z9X#3kPdVcbgv=dKfc;NiPJ zfDTODEE`J9AGP;%v^{O{@^FZTNgo8R5ZSEIG1kt63a59CP(th+^z`*b1#Z*wNw^LK z?lX5dU};Nkm@&(Cz~|rx*Q*)jG7SS@&5VhUc@RH&TKIHPe{`!=NJ)+ zN178JlhP1*R}V7-T;$)jsF)w!n`Su5z@&dGxF#emR=OP}S7*A2UE(|)qyx_yS85Yt zOuVpCSiDZvSm=}Y&ORqCx;~Rfp0_!^$on>xif#f9(Q(ht#p^@;ZmBCteRZD9F0E{0 z+Xu2#dX>hSVMRKx^rQ{#6uZk6(XV4g9MoApd$7$iKH<__Ol^R-G9L zqyrK%f$pX<(zDR9K4UMmm-Js$24-eD(1GzlRysC9W)=oIhEGqr1JxRq_0L)gB#nWY z@c;h)Kh?osTK{ur3s#+lo&jVNc4js@MnYzgbG~3~>`Zh_gp5FT5M*LvrDOO5pE&-W z$H>MAD)++ol=tucUP}Cdpv_A!GMSkeLH_gtUnp!WPc>izvVrJqjPy@F#0I2eCuDv0 zCsqbV5QUwd2~3)OrRn!7X|{^p2;t@c!sRc`g*DPi|+nv{oh;l^a|38(3c+Z2WMaf zU6_&iscuiYdZGS956?9Q^(6K`8hx?FpOXK@&GO8_#QM~BCgx}OY@rvvm)sX!z2MLA zFU$YgNb%HrnLrZ$FZ}ZF+^aV1oK1E?h`lpD@ z408DY`C|q3M<%AH{{NK2$^eSigsjhHp3>~k{{OTm`l%m1lV1eBz$ccM%lw7?os03g zL%qOf@qd%R_*}`qlR96pXAZ{apvd?V z>7UK=_uLl+F+X!Y!@vCO&l(*xc7PP}tQ1C8PzYszhK#_caLNdZVGUx8hXLm z7}-I&Pv&4{V*m+dV|fbiFBb*^L66X1&=<(U_GGb_x<6_15B9A0zqg2)>1i~268dDP z=l`G%0UGiDX5T03pE560_9xrFP+qL^T<%%+GxnU%#QbD^CQu1B(763n8&KX~KKAFH zG^{!U=!Op?0}H4@PZ$&1vv&Spz$YpH__2bzI7ru^nt^6H&+vu93Oc=l1ysv_ScVle zzJjd4!UD1q3+UbGC$&G>4m3jojlUo}(}PkVS)g1{&jwkT{mGtB`7iQbO8$|}@DyyA znf^=m6BpM8%ZvClMS zQ0!uRPP09Icyr|(38!rqfQK}rJAK#F8wds5E}4!R1+xq$RfZU*viP#k{de1YuDOizyS zHwrT-Vgm^oK%wJl^1}v#|BU~g@*{1&EFSzitd9qq4 zirR*T=;ecXj@$<66Z^-e%$;tJoMzMFstc^d?;5J1sz2e)Aflk=8t5Taz3#w(uVl>$ zalm@oyU2t2C24;{F*uY#%!S}IcwK?>a&tR)k@i5HfL)*3TAx%{XUahSY(-3wQo{H~ z2gU)sY4LFaBX%I9ppn;e_3|kTA?rbu>lmNLNP}jnf~a#U2NN_i2haC3V1rcP8ziJ) zjd%wKiwr|w!C(&_@AbZLPgdn|lvK|W5&Gz^-|Y9!5kVYIPwzWoSl-K;k1k+>S?{J7 zC(*Q_8ANmxp#JXM@B8&P-kWTIGssul0IQ(lappLDAF6%# z1|HNQ^sPMMv?ov_EDvORTqkvsM;|+vcMu1Y8N%ZCdBSZ?+Rl;yBg(H0h z_AUs}io54`GUab$3$7ycz;GW@3tpJf67;&2`XbQ@ym0jq841Tr@q`r|yil$s2(Oj; zK9Nun-2*Kvc-3+hvXuH>pb-~Lsmc?^MolHu6OD9eot5w8<0BDW;1IeQ9DDdF?E6D_rB|Zl^+qd;GO^m1@@tdUiAI+1JVj_AVa{Cg=n#F8CuUGhW^3H9PC}q zxmMgU0i$3nBOdJ>eRI}5OcY{m4cY|&mbY^An!ju7;x)hU+5^rE z(K**+*JD#sMus+$r6FR^J8i=NJ8#Mrs3U8e+lN$9d1Z>89*qZ&bH0Ns&*^#C6JaoK zF5VnUxC-sD8lp+6bDd5P)VejYBlzJA3OF zmKyEdy|$UXrBP3i(r`zzPQcBVA1bOowS{2~uhy?G5_U3XMtEIEt(3244P|r{%&8;? zG2f?BnrhD`N3u5huD0{-CO0M(&G8J(&LBDw!|1jB&m=hNn^lxDnFz}Z>%6**_r!@VfY{Pkr3KJ2ILN z3HberRX3s9G#nEx3e=Y2_SYKa^|TXpIR^*xc#qj)M*1M6SUumH1*??(dhOQ9Y(KDT z{oBMjw?-#4pZ@SYLj>@9W=TgqxA!yZwIC_>82A(b$FVu zep?9^#baT9@L_N5*`tra7OFpNy8R_Vs>=!X;9UjfxUPaR5|*emz4%HY8u0qSOu+>R zB+yQzjFCrSWU{$Ox2MKxwwbau{B7q}Twjmwn(H)bL>bd}0oOZ>G%u{DnbVKi=_VD> zO8<@hM_zBh3H7uH%YKB2)1vd9-xq2*H)LgoGI~)Fo%vwd^kn!J*Y#4ubR2=ZynSdy zxWrN+Nr}TGj9SOFtzOlGAGd2o;59}9Jzk~j2Z3OHTwOn_$R%TmyQeeU?IZYVuA1JI zmuZTmox%~h?XIQaUc9Xzusf2+T3UEui%rQQq}=@)l+&^mkKaGBKNilIc6BE$nZUbi zv!TdgvBFXbt9cRb56hO4J=O~nL$~3!8-c^!E(FGw2OI=D-)kX zAe?39u>D?HvxQRZxXa@FX1>d}DK!k4s*zvA)mnH(>RUgNMO-2#$<)n5hbVu}muTUd ze|M@^_w?)(qpt5aM?%{|9*5Q}L67Z)tQY}x7-7D3k&JP#8qj~1P{(n{+j}p!&az#m^5%iUmArCA2HbhwHfWSW zZ2AB-G_{|SgX|(NBM)jSvNHL8#Lzuhqap1LyIV*HD;E<@*uK zQvIiDI^bzgB;# zm1cpswrLq3AzLxzhPJH&Th8-qSaWJmS+d5miTG+`1lP8tJ*M%R&?S~YNgLt1^_JdL z!;kIFPKBhdl$8)OPF!oMC=6U90wD*JB6lH1^TT*KgRC2Mp4Kgks0K~UHkPFmKUILA z7;C$A4}2brYN0oS&@zOpn1KD_*B`?29wkBVvGR*Qs9E6D6-wlKY^v-lI)&W9!F8Ve zI7c3rB?%HBIaN687P|N%Xb3Jb#ypb_&*QxBc$T~>J0i|B##q;4h7B!2_qy2Kd}!UltVo6>8%&`%&j(V-z&lG4e% zYp!CM`-^sqAtj;E)<{D~2vgHu5kIt*v4+S41&uGY_-9Td-$yLSo!E8ZYNoLi^<8l) z(R1!(N$%=Fnrmbj_WF<$?7O|94Om%iH}|FuM!Fh?NwW6^dvaMHXKa7%3ZqR`%^Xp$ zWI6YPi8z^KmjuN}aSBv?%Zb0eMH!r%IIwC9FY+bNt$v#lj{A<>O`lRzbPl7X9P#%S zw2-obtNBkkFmN~;@sHUV`0ADGeZ=TAoWZxgY9*0I6ZXcxu=}~n&{6M<)u9Z>h~pbv zBrpLPeRAtRY01@*l6~!dlr=W~o>ZiAd(&J{q!`w*(=6-pg-2EiGT-hHjo{=F-{;F7 z86{Ew04xmLC#o;qLBp@c-=BD3UuuaVaFiHUmzj)zfZY*>{DhM|vHBYkufi0+C97-2 zL-oT=`eE>bGK0!!_40jEf}yY?S(4XIqfuh$Va>Ww$}}yeR_^EA_MatU>!^*YZm$o> z78<{HHuYX^JcI?1%GUC!*=Rai$VJ4w!w9IxByR7P0;~h47>{wDyT#=Qhrd(dXM?7)9{SuYZl>QF;1-lUia=#m0@oyXwOjK!B)@?PuXA_} z_sFEMbZGH6_89U&R*v`O5iue+U&_CF_j6PVl3KB zq0_y5{Ymp~DLkaeR?D9JP<0!ZI~mPjn?k3YW`EWkLAc47fKNnYfrXt3nWT<%byIKZ zXQO`TCrgau;gQzfxE>&qxOAS2)}?bs9VMoD9p0XOO&#Ux(JzA@ zZx{8N6d0OGKE8POvtH@c$1#qSBN*n#>MEkEL{+`!s+Nu{rwa7<)Y9Ttc!*$VHYJBw zinnm2n({xY)r!CF1gCT=XTE`mTx^Lppv>nPUE63($$sr{(Xe89-J+gfC@oETuQa;5 zjk-i1iE(7wIjQ|xzGSWGa)o!QC-1YDRU7!y$P6M^_^!gxZxs?s{{Frg>D;$wb9#R0 zkGwxvef0^bjnSP8>1@osS(mRcHtU+3=IViycp5F7h3IS7(gHJE7f7!JBh@BJXk)<_w~|tBR3Ht#WMq)r_;0)}p;8U?Yz; z#md95cC&qnNFb-QZ|i#cR1)(hi}#`)Lyk#}^vyaO00XlWZwXr5_Ju;#hWFEuTed}5 zjma6D<%l!% zm@sW=?wE?q8G!;l|4x}+W0*%Cn-{ii!CkE%;c}**`I7y1A5n@U(b`p<$#AS4SLyJf zy2OD8fMm<$@>y~=XszW)49o#qTNVzf-NMMU8VclGDustGnRT>GbCHUnEechnNxRUpTvmzLR*a;@8F>p-* zeYAJk6fo7 z>c-2a-C3h)GJLY-fSfH-Xr4Iz(C&UeiId$)dPM!cG3+qGP>pXHLu4%lj1sf(jX_eD zDK0A=-8Epq zozJyQ#8vovlGQHxPq9rjeQQ+1T*^Mll z`?5SW+e@S|twp6Z5iU_*5YydbGuv&;cZswuwY43T;Dqd#Hle}bY`VBlA}y(6@d>!f1I7kGe-7~)XMCJS~Oqn zb(G6|r|iwG*i08}*~F)m6avZpRUzE)6uGhH{VCY?TsTg977j+vjEIF=r-<4 z6`}nHBi5xI=i}QGY9njl$0x#6E}(-9SIXX^s>cmAolLyjgOm0!URoOI6H+xIoRGJO zk}pN7_!-As!Jo0Gdr=m%5eb>&t}3l}g>p^B7-+KmL&DX3*01_}G1 zDC_~~c@+qla6W2A(g6#}<>1b3&!yPotu0*@x}5DUE3ZVSQ1W7BfDTi{OCnB3Ms4tr z-?QbS?`^i)>z1Ff)-(EW)Eh$S(;D1{?;xD+ZHTkh9bgr5PNmlXwNbapt^^wZ-r+pU zIi5V8-djL$EYGrSG0Q_uK|Rw~1fk{46rY+`o75;!<~&fq9h+*VUNbmPej?!Lyi}PP zzStjHZJ3m5D>StN*B!RXOugidGZ4bzi@I!bt7iN;O(J_Z;AWFYwU|-v^i$0eexuY( z+axn;vYZ5N zmE!lRBnejQBIgE8=YL~yhz?M`xv*Eia_Bo}8?Cuhyv-*lwx((Fo z;T8cuvt59Ui)k*k>eZWLUhlD4F|yP&1i$SOi3zCVMw$}Emd}BQX*z~3Z?IT?JV(c!LPd~;=sOs0?=M=zeYKTZ)c7!4VKe9C3-``b#dzf9<@ zr~0JjZ*_X^G|`!A!&)dtF^I_&GNMgz169F{1kLN8n0#V>WK6i3C|6V}Sl~&i+|4?D5)cE< z9KQkJXj7aU=~M11H5m(UYGtI|I`+$D1%jneNVl?9Z3O9HVA-41u-38@$vp~6Qm4%Q z^QfF8T6`{)1rYEjow+I*j9VT!3~&N;*)rZ{-&lPllnXNqVu2W=qMthN>nSgkD66jE0M}%J8qZ}<+sN29pbGbnLT(Aw04lPBH$=Fx#oY%< zI=Qx*H|?Rl9#b(WX(wd?&Ny#FAZX%(93om!itu!LW~I&XEet?)jzIXk8n3>VyK7Q0 zfFx)a7pxH>~S?2((LxLX#wi+70Pav|_v9AWYz00e#v~*vm z9wh(vD^e_+I|J52Iikr>uIgHLK&A6{sSL4C;uUv=HDW#`=B)|&zJ%*H$Lu3LM~yU( zRC=Ei_YnRrnr{BiPh{eH964L8P_rs?iGc5 z>MMEl-^>~qktIws+;_YvAFqv*u_GjK7F$%`lyPdy_BT%k2t2s0-%lWoJrHZ7Wyl3a z2M$cXN%c$%_+*B`UGP)d#Y6aJ*N^k}#6)X=)&u_BXS#1axh2~3jZ2^m;1aff^^PITAWr+;+1lwg+9?qPFs zUB|mGlR8NRbJJKa1BwitP9o3ZJ1n~T^3l51=J`;|`fhFgz*(j7-(=XsyrY@#v5(js zxuAYCzIpPolKlPMga}ssYsGhnHSwJL;AdEV^Q}ml!mKy9=8Z$_m8Rldsc(jY*IYL@ zz9XhpY`hwxo0hjiRMpd=YcS^?RS+x^$At!89 zcf0iT$3dg)q(uvkICW1*4O}$*h9yHW}PP>YU78wI<*3d#o{m=DQsiMYhoKnXh|kj9j=l z71upcXikJ9Nj0`j?OTq z!Z%lrg7qx<*s7B0?(SzUvyo=*w;wJXy9h*MOxd=4%9wUi1nIvrj16llITu_{GHv+Q z)K0*;>6jH8pm0IXo`AJAu9^D{I*xEE+5$Swhr)mh$qz~bquImNSrW1b`1Wsx{Q^n{ zG6DedwDR$Q z8+cWkIS;cB<^T>G*KSxMXBlxF4#CclY@)axX@y0v;=P)4J-UE(qbhMvC1;%ESk?xt zl5?gcFCRR2-a$|*{4AnnqrT$%HfQRSULEYyy6ZUKjw3Bwit)Vx1KmC<%~OrH>@MZD z&1S$VI;Z|vsf4Ui)n>8Q&kXK|BkGrKGZl{Vp`r zE27cW`=xJ$lxr6*S@7MYjjCH0XJ5R*RDd9Ho^?s{QB5J zX;+DHOU1UDaz74ggKF{Q$VLw^y?20=OR*&ZrrOZ^^4#ooU(Ms$k5C_POfFA5V`!8WD%f5@Y8^vvxlOVI(-Z8b7fUKk(ZRK_=mdL&J zPq{Qf7Zt+JV&o%P3iB44mQEe{j#=5I4B0IOiSUd;3RH-g z3jHF;&h^_Q?^F8|vk%(RimT{dZ^Nd<`b>|%YT?F?wpZrPkf&uGTz7CocChjX z?ze|I?M6f8IG`^Cm-^cHI7M~P` zLvbcHPc}IQ6PS4>Axs8vU8ZEp;oY_~K#bVkP)B9V85K+_*C|MmN=vw|+vRQaxyxbShCbU5J;S zvLwri&&sR@)t(s;)m1c5I@WKP9K zKF5!d{(#j9lNKvJZ;k*k(P@yJ!>rtiY-}g%YNxc=ld>B+iK%YIH}k!XElMXTS~e!y z_JqYopBWBM{C-rvPJxo|d#e9Qmo7{J4$2ZKDi5Sha$NRl`-EFguwcq+>l_pQjSxNn zC9H;2xyhR(iZcwV@nhE^llXfaV_{2;qn+ZjGZ=QP9js=_F$1$D7HezydiU<^G4ZTn zcfxF!=+qVrqlZpi-FIwgr$6*5Ojk^%PNHQIXv@rVyWSwU+h&2jX9Mpsa#2kcJBi5_ zTVD}h3ZKdnpTCvvjrko&QSJcyox2y_ycxRU&SDvy!UCM)SDt+8Q2nwUw%uTgz;K{f zp85T2DDaXGhZYq=7F^6+3VL zcXyw#AB5Rcu9T>o74M^IeAQ#5#T)Ea87OtHh4K4}+h`erOWloOMUNIF6?54MsGA;& z<9d*xy6fXU(Or`m?nK%qd{wO9jcD?f3Bd+%frLR8~hjY|t38qsC#P-;866V4ov1@kQu`izVLeMo8!SA69?zC9GkBu& zRGPEO`REgfkg1uCJ7qyQta7EflI&S~=8JFyNewW3KWgo?3XJq#Eg{dO99h$wN+!Nd zR#+tYU8`$Hv$w#a*RSGkl>s$Ehb5mLBjdT^OWjJ=NbBxje!qkFxF0uSC-a%KCXs@j zhNn$0Q8LxUFD1Q`fl!U8=Mv5t7G|MZmX1ApJm@lrvzxXGs61J0eE#e(C`EOu00CW!c15T4BB`DJ; zgE324XJ7&*T&VtIs=n3wvn4PkNmv@^_Zd4^J4>d*_)I}aO4(d(wmu}qDh|=LJcSA) z<0`8z+Idk~RhYLmu%-=b`d(5@Sn`(mgLHIttLeJ6!uL&TxuVU}*ySpfft+y5%SwuX z@=$rGSw3m~=?}WN9#kFtx4&gIG;(=@S}T3KSPUlH9NF8zLsU%CaOm0Bg7QpHmX}%S zmVC%-(nk!vm&#As)PMO-h_tHaijtoyjkR*PBkQ+aai#P;1kH7OuR&J~slIhq+Mezr zzety``!x?qv(Z~}0sJ>;^C&cbC# zQxSc3Rr?HOjQ}KsPr`SBS&y@lm~%UzgBPIR_}hpX(DaDWeVJ1u9wv5RwJGl!3P8TKj(-x zMoxDetH{%KT$;neU?3qLQXMmsdlXAifkS>Kv^W!pd->A@#oi_Y0JvdH7=Ss*cW_3Nz_6ZR277-Yc%C5HLg0E zKe7~=!oWdiCX0=ZI{{E6zih$kQ*QS1KCr7RM=P;1c?Hi zno=)M&?|Q%QTqY7jmPf>J+H}2W;`hzi^bw`J|;@{RohEkH1g_y(0ZFWo3-6lqDc8` z=+Z$oq2GU_3qr+*>}Xd6=~ZQXx{*A;wiCHIYx+PbxHP*WDUiWb9sAw)U+Xl;8+*&2 zP9Ka9J{bx0WeAzF;qOQe_f6=Yu9`?yb=Oa+x}u^ zad>JHS%IKI<1WY+uv_nUdzGYHIld>e>fO|lD4de~OOWc-=?JfMmks~cm7*c^q)>mK|4gdl zhb=6@9K)O|6LKbNm8Uz4%7>~#VB(x+e=2Gc9@drzdH=n5XIh=G`xsCe>@*E$FkV3@ zu_ZO6YFVzq@t5Zw7a_`*Sz*u?0UmomiRh$bsaLS0>%E=bS>0g&cYr;Nt2rdW6(e4> zogU`T71V?u$xdaP+oVh;oFti>+be|r7sL14x6o1up%w*%(h>HAOsOACrqM&@5KZmC z!{nV=@?^z#&_b1F9u96!VEpk759OnOKSEAN9?t&r>tAetf6MpfV2G!K=mZUH^=wS6 z>@00SJNZCqDIIeILVz-#gq)lRjesP-3J|pGPuNVy$d-`lg@d2p(uq)=mXRJvNXziF zfgA`rPKTX|XfHWPhOv%~yn!7dC4djW4-fzd0)zn~08xM>KnfrY zkO$}hbOCw*Jxeo73lKCn*8%7Q3;>1zLlXzkKTCTXfDynLU;_9IFawwaEC7}OOA7;l z6~Icz#=yeNz|igmvw5l}z{mqgbTHdLA4d$@X9;8mHSlHE<+y=mMD!*Vt{{!gad_+JUB3MFR z=#8x~{|+-C7~Q)AEwKzRs@Ig*76#r>KUB5N(h;xll`vqu5c^;eJ8MrNvyfrDE=pjH zyjaHH1v`9EJC)}p1T(FD3$6X;OIsv*npRj$1Pi6tR!dmXC)`Ion=cv3$2njcPSNvV zjY6jAs4Ix2{1XnGh{Z^dKSpM$g+zU5rQvh7#v|zwS?*s6;z`P0{h+wdm zx^4h|V7$XEGGJik*S;+<4@j_Z51`|Gx7%A*DKXEq&Yeu=T4b=md>+3zB?h)2AO2cF z;y)UU@FZG%w?ly5zL^o>ne{~wj8NpRIpg6514i=>06=c6JtZh*vLFDkr#T7LbP?HqtsvMgK!{KfDqCXq z?sFDARtOpI`sEL{<6jC`DHs%5xthePy6z5L>$sE4*%N@4`+2J06Foy@%BR`-;T+^0 zP5R82LL${`UiRM|nLA$Nv_oP0GB%Z1TvG!8uxQ|t&nLkDuCDy1W6#{{}0Aho9+eiE^(!9c6>kKot z50|};2sXm$#9w3#Mre*oZ@T6=UE=!mtZ8wM zZ+;v&2Xpb{&j4hDqtJA2xch_0*0_=mZCfBbz~v*?mxaYp-_R#)C6wb}`+cE1z${_3-e`I-D9{E}Ii zt}mXnn5BJ*^G^EgIgR#;Lw!&Cu{@CwQT=KAb)KCcfBm}B$;8J`ppQf3)+yC+=N1S5 z!WQDwgm8mfBszx{t{yQsypBTqG@bAP5R09H-}}im;ita%a?k1zhM3*`ul}6&{I(|j z8&_HNc&7?Gyp!hib@M1vJcifm^*zPkEzG5XzeEnrYf1|^3d^S`TSir6d`P<|rfxCC zp%3Az#4&q^C(K*ajQOSOi>2j}g$uJ4-2=5Z^gLag(b2)wsz@grHl=BmOH zdId@B+^uTSV1+b{t;||a`R?+yNlGUorUZjK1}R1bmtay_LhlJ=XmRhWleyw%awnO< z9*uz#m#)v^%9=mhQV(Cfw)qKl9$PrV*b9xvm+hY+P3$&cHjiRjt2Y87K59cp_?me-hXdxl9&Q-ZlnHMk zGh0-E)EnaX1k{b3xvab_Sn983c<{UNa0kEj@V_v3pL!^vSAFJ}NcA;58T81!*U{50 z8F6;$i;Z&3nwY$kSUYK2F~Hp>fG{K%#O~EYw1%E+Uo}i_yVb;FZdjNS-9qRJB^a-Q zM^&9`VRBsH=9!rhp-4~Zg!n(Col~r6QIut`ZN6*Uwr$(C@4L2b+qP}nwr#UtrPArH zbam26|DLaNc6M@-G1nXmEWR$MlThUNz?yG~e4VW@RNs152&_ZLrattH4zq+TD7HS_wWus7$URE@D&!r9(FKH8HB z2}o=@9z-zk>Qq|ji7oz0H3P93f{5Dj(h>|LkCI8%pv;0b1#sIEs-A>E8m0OAd5;NN zMYVUhX%Qc8s}s_{GVzwa7_Fe?9!H{1R``gijIaLDOl)CU5P@72ZK@igDEnQs*QMKmxUhmfU0h=va0VsT|_*_wHj$G@xZ)UEzx#D6M=U+DxW zHPGypFznK*&T~*30aM`B{*heVWc2&1O2*>n(2x<()jIUZ#3VjpC|z=5*_i~q1u0Ty z8!E>;wq;%VXmc@Vtb9?Yu=HcNCum%qmk3A;Z`E*?IStnX3{lXQI(hou0`o{0*b_x{q@6H*G-? zFDKQm+DoVN?K4#9Ho47*?|8ZlUW&_q6$!?Yf2dU1KQt$M`l!i<7UzW|KnHK~Mh#(n zg+$E9Kf<;$3xJn7LqB*Q4a*4S{3}}>DH)P7oNV#yR#-jqDg6N9q>}ldnXV56O%o<^~=2t$8>eez$wyaOuA%inUPEH+MF&-P^E$D*hr{7v(u z4^`dX{X}r|LU*xj&1vLFO7p>)z!cSn!;?<4`Jnu(i~l-Q3PX0)O0PWEZRlBN2tNRurt>CAe;G0|^<^t<=N_3kTwWSSW|53LV-Q%O)HhFjuDY2k{P2h3^EE78MX`!V61GLQiaabx`J2#ub+z0t89K)vMzPwvR(m)u+1U{@EO_M4}FL!==K=+7kRkn`QWzC&X9f31&L=s0g0Yzn|P zQibG(q=f$sp{9gn038K^z$45aWtYb+v#h9dX4vHp8~50=p0K9>zWX0e!*ry}SLbOJ z)DV@YT3E_iuw`A&=Xm4iD;^rmA3q8+_3JJ+=SUHP%OPU*m)>Gjg^LZbmB9n)jETN8 z(=2vPE`|icG>8skM2@fvehOu{5FIS-aZH5m%PJLH@dJ!J0TZL;Ip<5vJmS zRaR%8Wu$whcSs@Ol8jYJal$qwdB28?H8e6L$7l`N9=Ke6(vEtz#&eve%C+Ncn`fO` z4wdIC(O;_M*4Rm*5_&J=fKRVl`*pNXHl(K4@8=g*tB|1feHYXeZGwd&axT0IG%<-w zd|tb75yeBXVaZnOzj_*0>eRkw6vFw`anJ`5wJRS{KC7qm3UDO8jT09S9h1{}(Yf$p zAgSKlfqU}0zuklgXP;O4CnJW8tZ+;wzmV6Psx6N4^60qc1|{c%MokM1*=66Z;`R%RJ_wL`H$xHD40NP%hMrv$lQ+fP{*5V0l0yM*Hr46tJP)r3h>JAZ2WEi3eRj1} zH6r~Y(ZB;Q|Nhq9(P7usXx-^G@mOsX1M|i^<aBsmPRtH46-LlAe#4nwvf;8PM%!BAEl6^**eKIu38p}3 z+i_nPo`X!9`_`kFG9-0_Rq%4swp*%OiO?`}3hb5F#Le{A?qD2jCs|lbsgBlqgYDz0krm#&SS+5fa@%jqR%oR%{yO767RIB z2`^Qj+f3T9v*_;a$tx982Fqnd1gHai%0!$+c~n4 zlSwM4?)4WU&WGiKTxjIRlaTDZqztvSqZfKfk%eGr?TUe{WW&6h@BU*&*w_ho#E{Je zkM+z$A@CE0-nLMB$B9U{h_TT+2f z#8UMp)^?6zUSe0mIwBi3rOL1TEQOeVLwE1(!h=jfi$%@?Y*@oY;h_?=gO>~mh?14F zTrOCkJ9Q|^4pbxO&C||uRG?JeZm^(7KBKnIVeWT6;#1xP7)02v-JxHN>m)G`p2i+M ztF|5@DZD!o`^de*?JVPT=*q{XCicz9HGd!DoTt1VU~hf|4ZP9KM0HSIUhXJz&6BG8 zw_}-O31wYgbBIM*ybB%CABoox{|#AG_15)w_I5JFzqmvfkNAaTUVSMXqR!?^ZaU`8 z;bMQ#s8vKxqp8jH`ObD$&@>uy`xN%BIZiB!2(?pCnhg9bj9%R_3~TI%UB~yQHai_` zsuS#&*j@?4MHf-sgT6rX#a>4=1|ozuuOF{3`*Cc%0-C{d37YXxhH@Ihi8^7l;3!ae zEm3^2L8N|Gu^VPt;)eS?gflB)8pO1zklMD{JujWDu5 zn0vV%%S8$_XmTEk%4fSkuN;x`PESvanQu~&v2KgGL#W1>ab~n;D(gMi-vm;jJHN$5 znz?LG=udE&7W#<>^u~+#A?0P-ol%sP0TE4)yU3M4i--`6=r8B-eJ;Y3ggcguahKIn zqO7Aac13dCTy57-p-jk0@N3w5v`uxQ@H!6_&%MP}8io|1ovbsyZZBw=;ic5x51m{a zb=kJl12S)l6X@h#K!!`$pUVW_OBlXkI_rR9PrGP5ggYKqo_=V}n$&<|`uQkbrLdWO zNQiat8U0Lo8q&z)>bp|m+8GH@z0km*ym}PXj&LZgfWJ|dpW@2i-+qU%lT=#RA0Ifh z0q-7}8=m@JHUyg+whhZ?AVpxzn*}9a)0{?xE#ldkJKW(;343x|G^<2}dYr6dz`mHj zL2j4CS@eYK`pxL#b`p^uRFlZ$ZrejyShT!m+|4hPGk>Yyeu{tCPB~R8qTpBYM)jr` zaHxr;faGi{e;;|)eNk%#M|&ce{+BscY)pBT-m+YKQl_P!`W}+1CjvfMQ=1eoCQjFh znzkg}i!S`7UTHBOb-+hb=t`Y_Oh4$d<}222<0u40+SSs(yN=%Tckp-$uzE0%W<;P!hn~ zXU-%p7PvRayQ4S zT63#vPMR`KBQ5ilzT7@>DYA_n62gl#Gg`kk!U+l~UI9`HQW?){A}>{j(q;|qV*zUH zaRb&CQQo;TSGMC$dhY+(cDmyD*c0C!n<@F5jkkd{y_3kI{=2@qt+^kpnv&?jOcPwR zh{#bW)s12Qc@il);Uk*hV$q@6HjHCwh})wAlqQT}pzlMNR$n=rJyd^?7#gVGI>-c@2x0J&{G+c?P*m4^TOGc@NY``*-+t9xw zhqrc=-iGc;a@HWyXgP>L|4DG&PY=qN=a^2tanT`Cmk;NPItochV8mBc^YG~eR}>e& z*$rVdWIb0ZoO^ck5H{-iFbr5?+g@3za)0ZOE0BBmdHs;m zQLzMHS8D;kF^iFHrYOI}i9wfS*9| z68#Y{O8mOQawG%BRrAE2oHxm~75ml8zm&DO*_7sN><0c<6s~8yg-u%mO95fp5X!)! zbeYeKz}-r(FQ_M{HGXT7a<4egEMrJHH2LsCWG20hqg6OU4aPIC4a-}R)efxh*@3NG zV!=$NE3qbDk-l~J~dr)}SOM_iug_KT2Kz(1?aw}aNf{>#lNVW(m! zF->@fReE{QSOoG(fFbLFEOsqDDB0+fnMx^Ty5`=JcRwS6`?9rX;#A0`({E9^+vi|0 z#KT13Ix);M(r}zzgvGIyoFYrpnG`={xu&jdDp$;2YKt-+_&I1I+`QHSY~1&G{uf~5 z^y|Ob>&^auI-dXUC|^!cLs3KJKT$s8e~Vx z{(Haw$=(?l8UB0t&cO5^=kfn2IcPO<#@Xt5!P&HJGu&)hU$dw)+_bRXWW{2Mw&|?L z+>{7pd)+y`a>+ftPJdZ=r>uWRg!2Nu4*;=!hwn#u-L zE?D|SaR3Uk@rywl326av7cz6on;wNR60-7($gT&STMq=px3+e8}; z3xJ$Qfn6A|J!;tpPxJdvaL_wQqFq&u3S;ww@f)k~E4MK>GO%&z3w33ErIY0!1K`eu zoL6}LOS25{qm{`$H#9JO@@rb04O>kU6>KwI@S9uyi}D+p{(Be76@zvIfKvkR zna&5r=kxQLxbv$awY9P~Gc>RQ(>E{!Nbl^-{N(r9#)CHmcV`HuzLo*74aolr-ya3j z;xdCr;|DM21yJG=WB-dy4$kGL@RmE*8xGt@{t(Iz7**gK2Hi*g650kBb?{3dOA1gp zgl7mwFZn~D3Q+ij_mM{N8_H`+`IR4=9`Q?16PqsS8wT1}_Vw>!#TNl9K;Z@6$6EY` z@7duD1LjYD>~qx5+^EOMFTqP}R(6yr&%C9T0rVHuZ#_pRpCNC?^$*o| zJ;o>Bd91-pNDRN0Ro*#X=UUn?*~Ob4ZNtC4Air*GS3m5tF9t!~&5?_Gk9>r1zBQ+O zM?lUWr{`aCamm-YdD%>JL;r4qn*Q-m?v!t6dO9EHPebne;5469!>0oBWR%_UGtJ)-;a4Z}wd%?2N68FN@dO#RIxeR@kN98^Pjr?d9YA_o^W^-XYHq z`_uddr1RJ7)$gl25@Nm6Yk}E&+|zyNd2Zx1?B2=V%X_V)5&CHCp#9J4;DODFO6TDN^?P-57!u=Q8=;E0@xwf8Q2PjioO zna6D-$f7CIa)HH=1NKbH&<^^Bdi8)+LW=NXf?$s?I(ApY69rV4Y3122j>kV$OWa0y zel|r_N_)Eb#PFz=K{^<@B&I!nkV38mi59Vo+@nibA$0BO{U+jxLez%baV(PxSy$8x zz~!9B9I~R~Q`2N!AelD^dX;I3&S8k_-`M02T_!$}^6yd3)9#1F zwXKs(J@aM}Ey7nU%fLd=!Lm@2keB;!lyc!^)xG7j$jjIVRJl&x8OC&%f*jVSp>KyD zW_=Lkaqt-nBVgM&hg)6QBS}*7zxc6?bV_V1nG__+JTLUBGo<<8h=RsG)TzzvP)IU~ z+pz}6DrFY&OzUEgjb(>}I;<)A7ua+)qSln0qh+|mfC+19WJ7r@?h7lbw=;b)Rs>32 z7T}^Am@6RKx1_r6AbTj>$hMA~xH{7r1-2so@_OMN4`KxSAtaqzzK?q>#|y zj^)}Op_!4!rGJ3hj5R01({HGWcwmCnRfMjOgz#4v?0PkOFu$Iv%}2yp}d1D{?s?dk}(0K>L??xCt1H* z>M#>+)kec_OJF@#QO6)!TViuE4_{CiQBs~>fyO8$LuL&>6eF!|p$=kPutQYTS<48( zmG^XvY-Zn7D+)AD)Z5%bjP@LcE1+j8gMA_LVVsn?YF`^RqbHC7+>$)mt zWHOPzlGqy` zN;rCVZ)CI!X0vd|&1)o*B#@?{$n_``z=XlHaVCwN%*G&cLc5+$h)mcX57LlqxAiq` zY@L`V0iBE9;0$(394U4~dI|w%D`jTnUxuJ4>qto5f*XbCLu%#ewx~2fh6*KL!KOvp z8w0qud(5}FtZ*fl0{H91d@j{1F;~fn8rgE_6%pWPh%ZKxOSsegN~_2Zhog6s9vgznA+El)*_zk<-4(1MiX+#_q5Ni3eH(HV zg+kI-ZG%X-eI&K3j_2szpo}pt=#==2S?hsvC9Cgc{ooS=cjZYpYVipu=jMJ${k-`> z9`L#Zk$kIF?v;St8fUeGkJK_JC8&dzUmF2uQY3WZ0sVW!9)GAw5{hD*-1c!H!L!) z?bevfVpJ7Mq@CtLAVjrRg6j*!YTR9gu-{^62M4I>r4GDWVlM60?I%yR76H`M^;t>` zxnCS_NL#>Q`M{o;>z&4PjqNb@X!I%Ytf)gjD=C!6Kl{4Cf`kLM*2J}raun-|xhcCJ z(gvrFzFcdu^mt%3Tx#QDiS!&0b~fgs=#G~i~l-6d3wH^nV&4NDX?Sh3xdBSD+G zSIKAd%oSgy5 z`B!PkUdkSE!5H<3&Xid|)_=ZE9uQjqU9$jzQb1UKBR|wd!2_`PGVzA&6t)CY`xkei z9I!(aN@ZqB_|ZAlm*KjLcm86m6$NP=Mx|Qa#CORi9Sdmi)TbS!xM&opB5^J_Z0xpK zF}@ha^{UBS$l>)+sH1MGS$Gm<;B~K%rs;@f6fmAwbfBoiE{N(bWt$yr?Te`DCG+9z z^)xX1c4$`pa4(zMW?Y_5D*r(ELUzjU%xHWTYBV1w`F3{rLX*t%e4N@0xVA3tt8zd9 z@4iq@p~qr(-48B2n2Su}pAZx@-59`xYG9H#59-Bzb0}H`$(&iS#%41^I7gxkkYoWq z3(uZ2Q7!Vhw?@|l_2vA<9=NmuWx1OXI2$h{98b`t@R+=9C8fOn4g4_j|QZ-6>Z%W+DG2iL667-UT7}rn*Sah6llaOH3@8l`b z)9)BD?|m6I5dPO}0W|QH4^0SZ7X-=guU!tHc(Pii!n za<-*cC)$N%F#-5}4)?2@dZE2E6EbAGN;{4uleI+dQyD|~)HtIEwuOb0SFL1!18r1v zbAM+V^f5Q5pD~ra?YJo;=^qLB$p}Q#BlaEs5#gC@{v8n=5(Z8Zbs`3E)DLPPGc#yZbn6VQfZOo!WK*%71s-S=NN7p?X#y)00bHtSb~pg-c`;u| z3SGs)uP*JQ_B^1uVO8u*u}^f3H(vaxRUH05n7hIj&|0lvc`X(v6=6)oY z7Bdwzzp!bf|Kc{}h8f3Iedp#u_>e+hJamVX8+pG=2pQi~Bab-chO zi1ms`vV0jAN`Dt&IFJ_3ZP@tPZqYrfw%mp4@_oK1LcOyzK&CqRf$sAj3Y;xrC z4yEy06xMo;mNxK$*bJ@T?>)GbLLh z?ny6H`brhja7If<%dz?}$&l>3P^0KD<{$vuXh6T`3-_6-@DFgbB0}ty!`_4sYJ82M z%?WL{?aa>WrbelHIIYL$K7Tkc)n}Q(<6F$8;)vqA_Fm9BZ}{W45JA9B_vb50NR}Po zO2=`U^L`@m<)C8~c&`mf_YVb>Uu>Mw>k;(c2^Wo}M_0r`$~NcPcK1nWkG@O&mHkwZ zRb`g4rn}pF?KZ-=szzLEF?$OLqe_OnSc1Bgj4f-qVmiXYLCOjZK^7KTbWF9rTdR%n zKW<>Ku$mPgWPnsi(E;d*9 zTGv6CKc25Id}bqF*&40CzBvdAo_*wf{FdVUBC2i~RVYCK9pROGnnf1qP`&dDPw6Ds zTpl2mU`?BbC^d6e-xO##<(wWZxc^M-ZPUf`GvJeeuiLioc)wsRRc#(LJJTsldh4&p zQoWR8e-@SsXo_oB)wkg-maj+fTV2So^7m5Xn3WWNy3|E28z`y2w*b3SMW2$7Hp>yy z5RV9i4Iq99FEXnVb2ttoC%q@DCX0lzF``vlA5f^4yr%BR$TZ&eCWplHs#KJ#QJVD> z1U5l)TE(D0B%=>Z{@I37uJ=+3&%%79`MQL=Sb&HNP=*g?a#1 z!JGO(WvRy{d?~=+y`YYjZB?Vj#JCyYggYdpuZ_}7Vb~ffd*i6ahW_4cUDdEeik{=j zs$FsIFg+TNjMI|axG-%(E`?ZZH|~4WPAz?pi#oFNmXH~F7wQ&r?*Gpwl-feQ+p;U3 zB5=S*K*A^VU~?mNEAF^VR8ZH#aJ9+4Iw-gISdu}{Gl_!x!AnxmkZWWh^lpi+y|s~? zC~o0eGk|O#x+|q_mAD6_C&Yz@x^7&NC;ab^Tf~ps*Nxg|m7ZlpSh^?aUMTqGAw~^Q z@;xqT;RQvLA^}YbI zxj5kA5Y^|%{=%3s?oxUXHW^F}v!ro%k<<_-VagcGo3)$cZ|QqDk?Dub0FPW3?d)cp zOnU{eJZkR7O&J|z^4FYGq%cD!Xm-`T+-9KfiiN6ejtfC(*0TC{0==&I7B_f>R-lPTvpzdU;-lQ!cN4Jqj7YOSsSK zpBo2e0QeQvaX_LTh@fpOW8UCCNf3J5mXlR8ybjWVc&r@mPPpRix^#P96eaVtKXIG@ta5+ny5VZaQk~U_pBEWPjKe~uRMCl9U2Tx~h_kA~>cI6(lg_-5&Y(m&*uhlGL*QewYS~X4@nL(Gg67l; zg1~)wPBUlx^Y*GA!A79u1iBl+>7zVIaeaM6L3zcnTVo94-R#f1Ua^VpFshDn1u>oT zE@;)gbVRI~FMax-eDGb#NCOVUBuJPOvr8x(cS7G#-iMXE4`Tjcwrx4X=SSpwB$9u8LSGY-WRIXGfGH(vKDXjMFXY)*LdvlW;<*`788Y`sM*H@tib(b=l6A<2a$`u6s&Xk4398}ZPi*wXM?f< z?y6IZFnyi^`)k;G|5YPB+LzR`gH3+hC-6T^Ljn7jh}OL5R6ze%Eaj{@OsWUs+*`8qW zS8@&N^6|wm;S4UOsfumFlLYo>Y*&e(pVjUVC(I!58lu%ycS=&s3A6&ws>aF~3Fg#V zFHd`2Wp^#@0^$IaKJgvOhdyqYE}Y5v^p#^`JVBeqtv4{*4C`6@1tchur}3noc4#&1 z_TUGzqrnhIj_hYm&rPkRX4qak39eF+&r&iy>V1;R!TS%v-zYS7dW?4xY(hDJ3zmU_ zT9B)1^ikr7ML&Y98dK(W0V6H5m~iONJ~Qm!Q!!x({1GSkB>Yq# zTQ#QiG@gX$Cmoh!mvHh3Xz`-XTrZk`hJZA!clfir*BHU^epkAaBwJC|{?Rf@@%L;j_}t26cb8zitxA=CalzRrH7NALj#`gd1ii=O|fV9;KWHsdd6zx zgFVctgn#bnt!F@{g>8s`=S)Z&!?Vm1D6l4tDZZ=4jzbWjGS(dw(K9dKhr%ue#{-~+YspyB%;feQ3va7Up5id}}>H2<|; zS7V%tbG%U+N}nJ?GAL_o|3TQdwE2}TIja5*b{_j}uzT^&PH;@5XN)+n#D8T>x@%e% z?(_|hcVRB=DtIBrTgBWu(>A0+0vA2(<)fizZc7}?M`O^F3(%bMa1^a97>dM3Rg9gn zV2J2fQQzZceKb#Xrs>s3%V>Rnz{pBE!po~#tGfEnVQ_{(b3qdlidk1f<6_8B{e3ge zuZT;*x+rk-ZFG%%l2r-)#@I^KM0f-HjK}T^wcdkK5Jccfc~MtW{-*X(gDd8BQE73L zHR(iC=USN4DISNB6_4;dRL3P^MUqTq5TZ3F-#e#%65|!SG#6{Y31q8TbOr9aV#;-j zk8Ou;y?2Jo$i&Bmazr_b7cIKnaSj)EjX~E+m{^GW-Qaj@5fTDuTMCUuA{jJGNZrh8 z_Fyg+9BU=FTW!@K7wkw5Bw|3rXJN5rHh3^GXeS9LG72||GN3=`Nk3Vy`cDbw#ES>E zNA#Xip0wJ_d8dL*1$ruaEHyyGA+F%q(%dTzF=9Ll37_cav59$*8xjmnvGs#&eWso` zH6ZT2wi;p?D_NoWHrC#mr?$n;1qfW6^0X*b7lag04K|pJRhQe4(N#=JiUWL539KwF z1ZlaI+3nJ1gX(uWgx<;{g@2kynC{BSD&Hj6hglFxamt$|1KR1wbA@LWZtk-R;8qM> zQT$5awtd$pZw*fU+hy&cv0Oz)+=SxpU|Vh*G$Hi`QKwyHpTDU@j~Onro>15I8(?D@ ze?FX!O)4JmOH@Cuj#2+ug8~@=Nix*%le=@RK`^l6G@$;f5nDx?FZ3r8ZBd{ai*FyL z3MCP4FJIPao_^}(9O+%x?QRpKoJ(?rxlSv~1<U zaq-^@wpLPtn%Nw88>X+u8=YXIOzslLw4qdCrXA@+^eB(hQ2R7Vp>B8MehYm<% zjd(DT3}WdGN&3x;C7k5q#@6NE#>N&~jxT+|$tK}l#s5K`A-0(#z^I#y;Y@0RwZ^w8 zTw(JQl{@S}E160-?4=hT8`L3rb&p2mUP0o5+}%5DaDId8kYY73T7OmXE@WgxZy!Px zvDCe9<~1J-p%F>l!C1UdzYaa`=?)?Sp$Evxj_vRjD07JEuB>1JJ)=s!SuI7Rn8Ju^vuvDGx(<$ zug8qd?iHVk-&vV54>1hbOsYs!ac|}e#~zYWDDv?AITj4T8PQqj~=%qsTF zK?yD_J8BAz_iaM!7+oF5R!g_)CtUwHyVLS}A1$g~=mwD$f=~&$v2^sSpsuV{0dkcV z-M9|RP4Ac#xx{(HX$F>nGc8!=FnF82jqg`T_A1e18vhJcs4HFhda}~X;ub_6bo<6I zXW-f^CS9<2sf_RInUTJ>O6knb`FMCbu4*Rw^_wcgEzRsq;+U5Yhs>l2^d22l4g0?!hW%^dgoPi(Q#8%rw1jV=s4f0t(y4jMjO z>(}TV%(}doL*%H;);79GZ;4UW!C8|qJ)^;USM2D4aX%E)Ku5<{L3CDu2v=S>^=1=w zrS^GNX+usUxn1;u%|$IpES-ZRZ~IOmH)R}=F6`H>Lnd+69?z;+nkkAUa|(xPG3dRlj!O?(lq#D@Lh0$Mg7R zTOo=Z@ujhbcovG6Z-J1y)t89oezNzfVgo7&BCTf!Z4YiRKh||3Z*pVEe?@2t@mO4@ zsp2YpN#-F>?YkhL#M8BeVE-tkLO1FnPYB)>hWVn~al*=MkEtv<(^Qn$6#YZ5Y`Z^MYyyQZr&>}Sq}4BNT9D9`JITPxSx^ILYcxu{cG2-s z!03OP&G1s+l$}I=&kxN+;?DxoKuP;e+3l6LJZwcI56#k$ezP|w?2JaZNN?e#RNL`f4%h+$Xe603*LfZ;uYs^N9h;!HiM zErlB^I5(*igA^EEAf9ooSWd6z{4#hC6+S_W^(y)PzD*Yv*czp#PO3%@XClP8G8@8hi5@0X z1#gAu7af7xL)W~E(iBFQ!Uka#6IifB@Bljm1j~gfTcxRy?_e3vd0Ce*f;Xej&+haY z+a>UWy;q48@F2Kyi49u_;wp4}6-rML=*3PgUM#0BOemvFKrsv1P8|I`NSzlf9yC-cqOQFb|@&x+V1b)a4$p|i_ z_TuKLQNZ)Mp4l)*Z&M>wA1|5r_=HY5XoDRCFIy3s4<27+^z0#mWr7jn3K^fM0V?r~ z*-24|mx_16RoS{L|8>8wY@>!czb2TF4P>)hiNm1JvKO%$kb!N@%-S{~-F#K9bvfO^ zskjaa_1)XJmM9dvC=3#b2}8uybh;^m9L{ifwsbO^@%@XPFFTD!PnuB@?w@)Qp@;I_n`Ted#LfVqdjh`lEH!+d2!?V@(?7P!AV*ldCO zhy(vuZ&yj zBy}kA7EVvs1n%qt$=}}A!KuG{)uqjmZAYcY;CGy*jafLEiTQtIWxNn3^7V#oIePXr z@IkA^OzZsC8!6g)!}p%v_CANr=m5IU)ae3x*nq(2sY*e4!E>(8Ye@r~5Mn0hFaC5Q z>|G0R_q{~m6+k_YDJ-ZSBoJ$3rc*R>5z+<=i^`vLz4l{W=c3q~lQPUWZhlEVkKKWe zpRktqSuii_=Pzf620(eXQK)MNs)5QrV_>VsR$@$kl2Ri^id0-r^}fz5Zztth5Vsoh zZc9lNqP#R6Oc_vGkOowF_MhLlOjTY^(PnHG4wA0bfUFW3CTHRez@02eCzjP*JYPLs zoODA$;W8IYC>LGs3CiLI6)0nKm0@!IEKJbrGN+k3 zzhTg3;Du*JF&Zpn_E!lJR6r<0T>rZo=co)bg)eG+?h`lMW@x#;bP=*;K+k@dA(~PL z_}2poTS!!~5skV)p_y+}B4L^A++HE7@0E)ScFW}z!2NRj#G9B|L=9Em@yW)Fh&_KRf2z8$$nuci6ac4V)6F9ekHgQ4#HM!%jc2fzWsCUcS7x%dTMqQk zccXVO2fk)xQ~WUX_$kmBq_=qJkHrqE(Jz5iQ~=I+Mm(G(Jy3h(TTx zR}^=&v}RX%s?(i*Fg6nvGCX`jDnIP+>H)Lbm90;&^EDIeQV6ByaY8&4<=zRtGH-|# z$eoIF>|TJV?TvQRr)cZiTTB+t7g$ECo8jU+3h6?Dp)w9hSB&u`r7%evj%RkmXb&07 z{#kQ4shXYO@j+O%AMuG#@iYoaz-C~v9h^3L48OQT$BYD_GvC6A6>Bcm) z#xZ3E9}Wv~_udosx!X5WtAK%g2x**o)Y#t_hax0bdmD$`)|K994P-xJ{C4IQ-|BU| zPwVHK)#B7#*xH>*ia!Ci#mt@WtP~<(L9=7_brUpc_9V`Stp?>R^1k%ImbQMUuLyq< zfjPNr5AmEs7p47nFP2?!$Hx}sBn?(P#QZpU1q{TeLfGs=%o&8ZMc1uND@!CnQh$q5 zTATrN+!b=sbNUxYP91XUo~vV--{h@j2G!=7S@;dBq#)8y!e|mXaw?PMG(hu!%R`I$ zxGP8dF(tjSS!^pVF=#`Et;ZsNDU5TK1TTxFD59@; z^M0N=pkvvbV-0L#7M>+v|T_w;`R?m6O26ZC-@D8yf6mWG#DMrVIMx$aF=#7S<6<2m4>Dx zg&mp6s|yLhzhfrn-;Pj7x7u_0j#E*6;Biil$M6wQlH2YkAu;Re>5qa; zs52<0a|k@UFc^JJySGPN+i!SYdY6aDf4D)CTO+WMKHKrth6D6=Rt}|g^byg>j4)6J z@KKiwVtW)nYCKM*e*`r`QV!-JtVnlDjA+$ajDQuLgAj$m&#pSQ<9xhiCIZnLBMk8E2rmM46f^~I+k)!e0AfwC-JpwNGZ@!4; z1sq^;oIAkrd4QaZ6p`EPv^2bpx5J60nI$3@)EIG&Qb;HB>4vljadx74q~1oUqLNlR zUK$0I zpL+jF>m0YvjMpQ_$2Ul*-eD$;RxhaLlY*QSYcptzh)YVEE#26*udzonbP8Hm@`O}GU231=EZ{U8&BskZ<$o4O;lu4X@~p@Z#@qJ7P0UDj~Mj%HcoFmh_#r@C0#~h;=D%Hb4&f z$cZ~3eDUODs;mirrpdvDMYarltxt^SSp%=cI)7Ny_Qe&twbgH?-ST-3Q}2WN4EZp2 zLX9Xi_z-&jMNF)Ik)|pa8j={PCv3rQPxouq=%}?W%acOlfrW+n9`*CNg|WjfNbCA+ zJts3Z{HmDpB&|Ozul$qp#w}wj92|FN2r34oY$P={>_?UEuyjRfW&)YkzbqFDTKDhp zl+&5*t^SA9hD9u}4k9+S;zN+2mVG!y%LvD4oS32!!R8&s_N|@uiKBkyl~CKRP#F~} z+Vc>8hmUu~w}xMVGGCFD1Y9fUn2BFo;cfR?@!(c)-ct~{f~0HXiE_YVCnJd3SPv#G z=g$oi1#qfb90~@{MGId*@MK=uatz8pHzso)4{?h0$Nhl;?S}{YidV1hHO6^mQ#1sA z{gOCra>UmF%2(}bqY*da5{@qpDH8A=ON4ECL5!NJ~x_@ z>8*qQc|@{psWBXV64!cF_->)q0|*cmF2n?;AWO43cs?DX9T_RMo-$5_uav*HIKD5_ z3Ssx)xpA5V^pG>N$rFGF-SF5pdS2#(HZip7ou%aEIT|};kU)Z|Cc|jU z;HV&^Rs~BtI*|ANd~`MlrXqW3F(On0?t_d4e{SCJcd^=J#NDSnHG{-Dkw(K`FeVOY zKcG_&c=VKE*Af#4J?yjSm)dNmr344okm};-k2bIcAGiKFK{y+dbsr8i1vk|L&fBm_ z{cE%Rt4PO@V=*>SRWY}>YN+qP}nwr$&Xc5IuSf7M&v zRbSWJT|K_DwZ>TEIk=ybIe+(@*R>uSz}PibuBJLT+$~5|KM*IwUGf9lI^w?Ksme)~ z&9_0@ny^wI7(|v6?T4xBt&PO`xjQl3q8QAdh!<-=%A4oa`=boAh|{6DR!0EBG?ztK zP+{=eEYRH;ddby|kg9d#ZOO$2+f_JapoRe%p4A;-*A+K6;4 z=Z{*2;MO@{LO?;t;FnT@sENJ69T~5!KMx-^1aZAm-&gG zvLq=ixgnf)+M^wH1I6-QLm|1_o#CPtZ*cd7|`c_Ws9{slu;*#>^p6fC-`xe#Bq1;2j+IWV=iM&a~~-ME;!#} zomd-g2bCCVaxSayB9)LU7M^Fj!v2}zJAwTZP?r99xdi9dZQs{QHU(&8CZAFZiXwng zSm)F!>&*+w38MV&jRaT%{BHne|7C~A_KlKb!YWq&O^{heP% zL&J*4%EbDA*m|P>kN9Ow|Ge}>|7}tI-|=OFe*>6_{ujVZhFb0$Vx~Z?_}_rc{)SKc z3upEh(d?ggr2c}MnYh}Rd{fE(Vw;)&*TR&Q={MZ$KS7+?SUB2K+kTVIzA*u&-{`b| zMV|fb?cdSQ{$=axZ`?B%YFBDELp$q#S;6{i<4O+lf30A#{+IjxZ~xmrG_$NM-)H)d z4J`U^aq&Oi{+WKpK*#cT$?Sh(nT>blSg>3ls#GQ8ORC^DfT8b4H?IqZ7Zq60iKv~R z(f?^4dTs#64=qS?wnFG$M?d04oJSEWqR=7#^tSP^HQjw>{Q2TJeb*9o_MCCVsMlwN zTBjz34i>-#dO-;l>t6!IMx=~)3m^bHK|xgHFWA#5i{3!Bftd@*qo9E2lLUtt1NH+x zrOdC6OeIzodZyRqRbN&F&6-C}__SCAV&;}dM>fiaK?mr`iPuGj1MAPF56BQg;Pqqj zkA{s+Uvh5g9UpRVETFS+n>hU-9suHmeC8b=p1hffoNQm{*(r3MldHP;=p%2nE^vw{ zM97$efCL~CgiIO}XYU?}KNHY&gEE(VGI8(T0k6%iUD56Bq#Jw1AJh0B%9xkFc28g0 zfJ6QB!MzNm6-UP(CmMxhTb zj)7SEH*G_-9f8+Qw(&{q-%tP@KH{=w8^e!2w~nf_eqseacAZ|mM4w6DPzSvmjXBhi zaOglK{qV;}#y@z80KWl~ zL#Fc1Apqd}@TCFRf5pVt0SNfCtOD|(Ip*%bL#95*6}jpA@I|J+@|KB3>|X~7fA-Xf z)w{e1e(~hVBDneTOs4~Yi$GY`1lIWlucOn`d+`I4b|1FRW)#NjgameDAvZqnGAC0d~*K--$>+1efe-O=%3maII=>oci2-hr9}< zu5Vd}y{@WU(8a;Ft(@WZA*(#4^n@`hA@_6&*SY>UibH+7|8Mz^^kqY`5NTmPdqRXW88_t76{s3V~tlO0!x(XzE}Sl8*e_hy+2 z3FngPdWFT`UM9E2515aC@5$eIH3zC6vT--`=W*@bDydD1R;S6W%N39{JSnlj<>mQG zXBUS)w`*(JK_1q|`Oo76UJEY!ZRK}>pp1-+O26CgW-^=PQWY0mr299?GVFps`07)h!YJO%x9{- zj@S;NQ{y-)-4Jz~77>LS{Bdct^vJm)6jDrbEL~+N7t4Il*(l}*Hg(Oczz*TEjbfNe za5I`%Ha*BLuLKBOQF11g3~0X%6bUK!b9^k*@p@LTcGSc<9foJZaXfgR%K*WAj7=&s9IrZNvFWKA3Sizc*Maa{H<7%Re zpAwlr{Q!3&639<%efx4^ZpZH`UkOmZOFF<$&-CWz{ z4UNLN+E@M~U*EOgW`Xgp=$BR#B{+Tz=Pwk~A6QBn4&&qpBwd@XlWZ&v(vxzpnX}2w zM0kd8L#bf(kkwf!M2@{ui);s??Xk8%x}wqzpk!)p9fh33QlNuMU8&!+pY8B&(?;#n zSv7GbK(xm4qE&0pwj|Fs9B%F0w1YP1qD(L5X*yTJa1jm;%mzF~OE3 zJIWHnE5D#tqpPHybwp2XOk4RhbTR|G1Si+_ke9a>3hsKqwE3{<2<>o4&SO)!1#sq= zW|9MgD-adq)9(Q6N2n+5w%^Cm?L@il4Zx=&fBeq1{Q9kHZI=XM8ivlnOKzfwpn)2m@JVe(L!mD(V_Yh+rK_iM zSt1Nl%SPMac|gS5Ot5cT7jvS?_~w>bqO^iJ!j7!yxsqi*ns-%YV}K=?fC>xf$ycIM zR?tgb7+;vuT1wHIJkGvM>~V_MaGX29D#}z6ZK+F-FY2{|c&1#YUe7;yyb&C5tNrj1 ztq!m=L29CZVO=zI-5RDHquPRg!@FZfP zP^ceEWjS4mU2R_^VZqU1sH2EEv9i{DOy;a*)vOZD4_62Y)YxlX{L@Z!6`Opb-9BH& z%Ds)LW***El)iSBZgzKXiUkJp@JvdX>=>4HRa~*vz>JS? ztFs4yhMn9kQnk1k_vH)U;p5@d^@2+2B$0ArbWhP4l@ch_ql#8-O4^!7NvoNphuJ_j zk>0GjVdzihnTw{;C<0`AhNF}_g>4nhaGpxBWbyk)e}a0k7uR%cB;G#P3S9jP>$gaA{Qr<|4gKEJEd8)wa~w`y-sOVq!>io zfxUqs#v6cl*DMTk;P+e|2z$`Fv!lg^%?!TzPOiAUE&0T8$mq831CtS+Xii zlb$%w>PxvQKA6Je4}q}nYL?tZZKWOcmu30&Ybv5fkhe0|Q%W%H;m!!A<20!% zJ~vdc0Nj5P$HNm0k<7fOx*a&HYO;8_$2R+-#dkDv=|Vr>{9eT*iV3SgP_(wSCHb00 z+<+#B7D9jYArN0%U~-xiUfa@2hq#x8v}niJ?WKh#&~EGEh_iulkY>C=jExl<@?D`N z>2#$EB_OC{N>%c1Q-^Y4O~vV^#VKCVN$$;fbwqR7T7aEU)3@qfPf_|kDaj(5{CR3* z?w(OT4$P zwEt{Jj3d2_%VGwVaAaEDEyMud}|A_KW z)wei`{b$d`9#ti*>CDex`)puO_JJL0Y)qG1%c8VSBdL}yo5a^Om2xCU+2hLSXr?In-zXRpl4k5+YO98!f7#n>5GU!*Wob6*d{i+j&d zj&+kZ!Rpb1S21EbpCypNk)WH7VyAw20JE`^wf<#-ftcJ%$y2Su#4I%BB;yg8t9a+} zhGz+HR#O(Xp*4E;5bq4N!IgZmxY`@}>L;dOc%uxcOOyL*w#If=bGn4PiWh{Rb_XKJ}7KOn|27PU`xu7+B@FY7oTV;D7wlhtiesz*DJ=_J^?n)nvl=0UZc+biT}ivtB`!7~ z*iZf76O@>8A)SyqfSA(-A;tZmt{>lWuvQr){t6f7ER*Jefq=Z;(pj9VbYhR4N3n~t z8zTVsu zsP++y9N7&%v%Pur zH^MyeRS}9)1E>Tls=s+}dm(}*13fT@~<<;t_-g2q23YgBrKe8B{-CAs}f`u8C zHcmy|)vNMVJbTy>t_yRB2{PCCCh!Len&sV_j05+A+AcgC4)W=Mdb>$6B6SzIwHGFf zSnvb(u?rXPa^}>ntJrss1YRCg#CnJcaiE^ALaU56K8`|axkV85oi+z+7Z=w)W$&>w zgh4YwW4>>YZP7Vt_I-{Ho~NbOV$)>MMnqX2h`m(`%pv+W8xu+DNGp(|O>PUNiQRo$ zzxwU6AB*~Nw34&r<#w+JKSS|PkhAM4xS|6jeyY2Kv=N5us_cis>8^2ftax;4_diP= zL(GUbzAK#~~AN73b#m-TknmltlfH`lbi)=|dtUIw$o#+VW>obV$+`DI7|gs%$#adOanfq6Zc4fy)v0T;XbS^&Dc<$du2$7n!C=p@Dfp~G3|LeZgj zcsPJ{d5tE3eoB3!6?s`&Ve$M{)A8(X{^~0D64UpD7})tji}CybGQk6(A>QMGJW)Pj zy{5K4#EXL8c{5)M2qHw}co5MNfam!{A&`lLaDX=!%yD)5;X)A$`5EH>d!w2N-Th4$ z@J~Q3O(VPaZMFO7h^j8c?=;Pt?e2Cjv@e|n?uixHKpwD+SexI7gYa}+-CtDibmJlW zqC*HG5n#C$Ksuzz7Bm4jbm#@p0eJvG5MXnFF;g@VKJgznd`0>-lP6H&E2yy zW#WD!Sl|vtb}9Dg zxqT@w9yFB-jJ99?(im-yV{CBm|CGxp-DJIve#}5eQ54>@$n*3#=QkC!P5-6(IS3(k zBhM>5S5+?sBOv@dV84)=v4O`7a(#hnvtM}GUfX_nzz3Lte5E}t=y9QM{IE_fX*Cw(dZ8Li)f%R=2Dk>~>TaR|AmCj09K zW$SWv{UyI21iN$58_qs8OFPxw`o!0eKXCta*e}(_u@Q<1$e2xBEFQNu6*vYXjvHMu zB`p9&JnCaM zB=WWcosXi6Fm(y)zVt*kc8G6^$2+{NK!Uv6>$WVKNsp~azpkckV^+_!?J(tBW>d)m zdZ~8L$f+2jHYaa%X= z50o>oeP6*O7mUymQ%+5)@0nX(X^cxuu;Y+Qs<*bNPB}>_(6;cl69;Z ztag4>Re4#@4Z+2(EQKYClX36VU8c9~u?MD*Cv6xtHu(aKe90!eT3G#{&MQ`2fyWtg zy;RvlXRMT2;i6KLaM6U4C#-OE++?;Wtd-3uSSfP5^>}k;j@dO`dG1@s69-}#oj-I_ z{=j54YO|x2Fx^c!Tshc_^|)7OBYYEXmR%>+r>37%Vq#)5Lqj+zErSNC3K+<%$xA2AgrFydFpU)cLsQVHQ(Yj7rI+;&OZU52 zc6g6H^^Ib`#FzcCcj7E3CGF)VYsHsuft%Mef5j^HnS+cQayG9P*G<|PdWep=!^HWCO&=#m>v-~Mko6h#@YqT&V zUydJFAMET;{gtAUJ%VWX@@S5%J$-3eK{aUT`(Vswv#qLqPsf8|lV!(JB@;!Xe(IS1 zeJIE{w~<#piPz`_3ScP%oCDU~ia#RtwxUgQ-hOPVW*EpXToHRnOpVbYgzP83h6cm z?8n~N2(mLVBYz50<(&q9Vsa>vL+L1#|zl#U2H>MCav5bR14E7 zW4*TIh~ipQQ1bQDTvRH|l~{gQuPob5giu_4V$1#k&@Kbt}3 z{IH%Q>-rj?)=XCmVI1y%(S_D@tlqnwwIY`%HYP5rq?@Y?jltedB7iWbFsQ#Kio2YT zL@N4P(cKnE>aUa(2s*b6*RJ7Z?Jk(F$w|KUcLG(i_XSbpA41HMh9_+J%cpmvMW=t6 z1H{+O{5D#%COai-Ex$2KM?$44vq0(DqHkSAC~XUv8n>2I30FYTNxri#RO2hHP2S=c ze8yvqFQ-717EG-G8GYKayzUcj#o{a+?xRt3fU62pZq$~>u3!6xaSdnS)UvwAIytc@ z5snFbrD0Cb9afS^M33l5+yA&YH9`USN`t-m6ZmSWf|rsanug`hPmnl+Y_K?owtk z>V9qU9XoVOLu}BP42Xb&jz1P+z(SQ`JJJOlaEu?1z#u$vk)V5~Q|i|+Ofe1d2pNwF zI{DPB0)f{^d&LqbbK~BV+)1`)$#o!l54TC=9gZIUI79T3qe_t5WnM5L!KqqOOw}O} zC=27lZtK(rwrn{%xPC-0WAY@7-!2(pu=hl>^A=J`qB!wEU8+9hcyUkN+Vo*pd!Z|) zKJE0niXJ@BO!RW`=`x4bwW+r8 z1x^N&1E0Ah(;NQAjz@K<{52?vbPl;qWEJaxOMsG>B5x^?5S*curd)|i&jbnyAM8v6 z3CTpvY1nd->7AjZd?ATPl$23|i*)tAs|=AU-+;)kC**Z!HLS~W5(K9(TgNhhl%vQQMzQqH&f3B5Cb^MfS;D%X| zljl}OQcRjInr63ozLkgCA_t`5IwJ1*`CPlJE126V6}L!gqZBlwVK{~-YX9Q0=b7E4 zU@Xt6VJpz&TuO^vuUhDm-HlepZ(9~d6Np^Ot!Av;_(lDwB(+Gq<&L@bX>F$iZnck~Gf@3H2`mk*A4UK#iZuH`y-*Dq*me|8emo2vRRu`#Nv zwF&HchcB~KSN0>Xf-RA$^|BI?UnM|xtEwR!3_CXt^z`A)RVfE2ONsi;%*lgErpfiK z7*>47bW7%#@A$ERlz_bx*!CcqNbX0}oALd{_3U?L5t`_V)giQb&A3k8DRbtEv7C1W z+~jcNw+fd2_GzOQC}^8paU;gwLJD$3L;G7V?0dfm9p-(@^K?c<4obJlmpX69y|aTI zf*6`=*cQHJ+sNir0z&i46F;0IBsMS8S+OA-g`*$KM~_#8JD!u1Mk1Z#4cq)*f$T%v zbJW_zn6t9ODqdJtPRP7f>auLQo}gBVptp*`J!4Up`mal9r1f8WbIfjw!wTT8I{0`@!!bWGE3pjR`5u&%Z zNBZ!ih8qjOO3GYuOMP>!vC~V-#_8B5&8EM4Pgk;BGjY15u;b0(7z!J^Vg1brrwN0a zcqkILYHDN4{c(LNfum3Z7{C8@5P+RW4w>mhd;5NzARtywh|tX)VnSWN!&RbpH4%t@ zi-wIQvh{7?yomt`->!vPcbsioRdSkR+tzvw`$T~`!3s_9gJk+KYm_PG*@p|uakoti zfi^VZ%|)ry2RG__C;E^P-Qm<=sIBz#E+YQtSnRtO+L}t7xYdWl1x%MKRqwQsCk@-X z5YAIi2b720J@n|W9DVC|dr#R1fxc^IUluF471q);sC+wO3!L?8!$}!|>9&HgkjZde zhBIK5UDD-1FMGRpZU6{E&wtY#eqWIPKQxC@VhU=1YYv(JJ^GdYZ|GN=Z+{yj9y7y# zJ_GUH1O8iw8{>Bm`9BqI^xtZ?|1|?4`d4fCH$9u_-&AaWZwVd$d3J#Hua@s$dceP2 zYX4}3|Iq_7vwZ*VKV}DL8EOCR>;N4N|6vc9V2(M=j!ySYKIOy7rI@@8nUx`q zAT+h15dFz`=qdzPPb2ggl8}OG0E3{FI73%RB8H8B&i0`$^US2=k)2x zJ^eBK(Oz}dRdsdOZv8`(8Lk3m;3gQG#R3O)z&8mXb@v1q5ddBoZzT@~dPE~-h@ZuR z9Rscjm8xE2sHvKh^2 z)NRI8qQ{?KSV!wOY>h4*;L|Ent`9pFr2SJbJS>bKK){Tv4>y)i&Fr%(5iyhh4;2q6 ze%)Sfxi=lF5@<-8jD7|ngBbYW$p~P)6yKqC8-sbBpW|OY&Xx~hbtQ_RA{p1crb@jJ zOeq3>zI6KaL&EO$YWVc|>QKEc%UM5TVu1w3&@5G}65~XM&RDJhN}%`n8L{}SHnSj} z`Uv#IVD{x-fuJ)Ok#L{|ug+Beru#Z%x%O_6$sm0s+OBq@IuYVu;g}xLLC1Tq$eQ1g zrlb!I7XWAnr%UEl%{_ms&HtpE$dWwb0cQuGVI&4(ph`v!P2wa-r31_31<>WU0R|8r z3Atfm3MOU;8p#L+LC2zUd(`3u0_@?huf0M9zP^?w%^#{i+VA$BT}nbihy)2e1FR@* zwIDJP0u~XFJO5Ic>u=FWnM`*VrYrU`0VSYE3;LzQrnWJdtBwsl(dBf!;q?|5d?YKB ztJZn|hPyxvVOMFBKV69$-onTy?XuY77Uza1S4(0((TWI1JMl)dY@v54?7kDf_OzH^)(qp1YD}X~3VDR%EZMz| zi@dxid_veDYlUR+IFe(Evs^z@;pvx7P!Dt|5$QYYfTG3v#w3zfZ z+KyS06e)rL{ln7aS|KLNMHt_7d^y}t*{rSQ&{NZ@b(5+L_22;C8O;>T2{?~|BI-;m z#GsH*RNFqV7!OO>Qq+Z{&E|_!E5=qUw?MbtC^@g-3tpshI@?Nb)oHoj0^D)yY8ClZ7D(jX%l6W2UnY~BiV{aV z*fCmlhw3P1Ocz*c*`F(cmL}t&)9gzeiA9ecG7mAH64!y@>%znf_oU6^rvh3Z3QXv5 zX6;a$R7?YTdpXX75al!a%(ked$5h(4wn;vmdDiwJGaqGnMqQVWEzi)8g*hu1>MX|~) z7b$86Ji1OxcK5TYu==iR3(9o(K2s}g`dRqdNd0-~36zLjdf#4CmJLIs_G>quIBdkZ zT7OYceQhMNEis8yisd5;%p(|roeX9fXUnE% z0aj|eb#iM$oV`A}`l2OgZkt#l9uM;N@^1W?VP#M&)@)iqy&3TdCB#!^8Ond#@lhgTUE2~r*l+&BA5ejPsE)h2GgHC*h zR)8>15QRsQ4x^Q-9&rHfb~m>_51qLg&G+MHcX#O3>8&gEboHA1ttKx(I(2z+OZq(6I_yzA%|FMI z?q-Gc-;vku_us6bdShiAl(nF~MB8=wn67MRivdJ2Z;h8;|Q{Kb$SNaZj!$u%MzpWn`~-=U$F*F2*i{jHLDOJUpNnTPq-^84o-rqn@iT zW-icw=#{D~&`}<&=KTq84{jT-9NyzETJcqF{I$C`+R1EX1RfWiCRp9eJo)9>EK`*o z*Qk5Y(bnsWjSHblyyJhmn6`WIl|>R&3X|F4{u_20^AS(*O1#G<2R`Jb-Zf1b~zrKe+N{7(V-ZiWgO z>vfcum%NzH&dEEMmoqF3mlzB!K|8&hlp-;}&dzY~zgc<@9=MLSCb^D2J@em$r_0nB zjTI}a-R{vPC9BGMDXbvqz{LDBHd0qrS3m%+5*Zj*R{_o`EG!U378b5FeT^=g@Q6j} zopS(uGPARP;9Z_W?(6{8)I-?V`fQ6V_&nhme`ho1n9cy3?yuc`E`YmHJj&`&^u#`K zsqHW`2pRo-6M#8@bu}PuZ#4h|*jNCk-kCN(N`7qmRAupqqyBbs@+qtgWbeT;u{;YH zcugi^B_@aWdD=D-hdcUmbOqAf?8yUg_=*#S0|;*bW@UW_pzu`^sTjkf!Gy0k_zJH0 zlA^l!vy)?S_^kZu?Rfh2Jc(tA?eR$rw10g52i5_E#FxUiO6m){?8kdE3t>)SW$>Q2 z`@zN}mIdWNW(BqnK$Ca6#b$wJr*BeO(4@%Yygz~x3aa!r{@==g~^ z-RHE-*MS0Ky`$aoz3nIanfB~g-?=sV29C12wGSX>OZWK>&-Ix%_sc6i=-O#zA!wz4*@olv0@o0Cf^Q`7;2NTA6_l z_)&%k$!;j{kgCWpTfHbV+*LlxDDT;R>=%(wEX>rQ|9-l^KY2G z)l}uHm_SDi_VqQ5jWv#q*Ea9#Q-e3j*dBi@HqW>HdJR*$@ohp{Qv*BG{ZrE?(@{2P z?3HVN9$x}9*m>96e_DP$`QJDHsF>Qd9)KyT0bupP9qCTA`7pF~@R*;N0<}s#;KoZq zd}f;LjtK{QR`q}aAa8Wb2OzJld+=cI{Yb{#|MMFCV+a1MIYOuVL3Jxb(m`p2phoW#^)a}=Mzh|^Bv-thsZFb ziT(XQCZEe>^`F5&*Qf>{ou)P6Y)?=-obbcGYNr%>p_P1ZrH{7hNtKW01 zDFEy(r_4-NHS;hX#-B+)F0Z_|B%GXTj+-4ecE&a=HGIEXz;<9;++3ZM%9H0|5vRL%dD3bh6&ejowV} za4j>5-AcKwxQTJ?%-s|3Fv|&k^5@X@2Zlm{fA+5PJgaYYf{4KNn+cOG|HqLDGR~(5%Kpc~PL%^PQ+G*X#j2Q?z%G&FcLL*h8 z9qDTMJEr8ec2_jrOB8yYJl_#WdD30m8wtc86ni>sVbed&_Pp9xmnfQ_v=3Cw55GvY zr13V9w~Qe!Tcv21#4NE4r3Ji*u<(qUAB_ zp^+I4MsTdxAz9IelF#Mi4NWL1YFKme{OMUl*Sj%hCFyU!!>*=ZuA>1Odn@3~#wB}_ zPTp^_USg>p|RrrnocxVno{I*Ni(@Q`yr&KEu+2o^SchiH)E z9Eaw$-0Iw_DX+K%j2_GhTmwJS$3Yp142T_8)A5;%rao5@W#d3fM4=tzR9tY#Z4$EE z=899y!d@{V_~3xB4#O$T85v|enm|YWY@E0t>aN=j*WU_fCFZ7Q} z$We*(PRkbT@3c0SC%yY)yn1@RN`>!eJ>iWAKNL)$QV<7;Q^A8x<8~ya^bQnu!Bi-O zk)J4cEF^*}D>4B~*<>dfuD~rz^SByC^Pten7{xN}IdTWx{r&@mrQoU%XEg-0W>ZFK zuJ@I3kW{=xc4tDRY;+$;_!2a8gh{k{feEkVp|m43_HLO@u#6)c%J{g#u;D$>RCjX* z#aao0dT*-wS8JG4blUvq!$qMLrYgR!om$1t7!%@TZH#m;7DZy!`hrN-2)}tX7X9TnD-XBvL132M(nM6=(= zqCF`S>MW&%o2hLwOE(24HPk0F@fCGKRCb_N0w|5YDt5UN(m=p2uCf|&996cA8Mbl} zUEC3G)qN<_l?jbh;FHpawaYmQn&JRq|I*MpVSUz5rqH?dF7Ru_<<6}2&0jwlNHpND0m%4HMr%Hk};ewO1 zpgYPBW}NdL8N=gW(!_%lYN&3tB{??FY;Sr$4-2^&t){U7NopQP0ci7sF-J1>$u~GF zCto_!w)n*tkF4Sd(^G%--M7rmAbf=6$mP;@0O0#pf0AE>9L+%+!U4gr6^%wBk0E!U zIEMS9QDzR+@TQz2o}JUhQgs6g;}?Nr&+@j-Vv*GK@uq;vlXV&)iX{%hPT|?xq*l*J z`teV70U4|iglP<87>1a*qgu2Py(1CA^a~$588p5icg1W3N_r}wX5;O?>K!(I_)_ub2=!^BuQiXi11Wt(=n0-Hu^Zr z)^Wrp$1Na7(&z!0eL$xl(m&fB@ii_ ze<`&li&fRQc9UB`lvsFR5T)KOPel(0IWc3m;8z%T8<3P`7X#GAGi~s$6^Y|G)iX!Z zP6vEhdV614q3L7MO4jz7@C`1-1aacd@b~76Ip#K$e^;w(cyH2ADU57QJFO+Enba>2_(Blw!uVgG{seoV?dZ%SeUwSDM|{ zv}Ea$pONgpC-g38n4}MXo2cXXlxRzsOGqd@%PV&tEucx zGlG?pK2RPaJMHf01Yv$B(cwYYx1p~BC{wwHYyHAUhU9Y&p3k&Infm!#aYvEi^&4JZLOPXT>CaL#yMhkf0P>zdzv9<%QYmpe4#oa=6=^G{a^xeYKEtF z0RXJ3sb$c)9YSBN0X({u)iDtJ0F`T|kbXnWEh3u?$xPa%8IM7b%$79kLR+j$qe`qf zMUZ?a{-?T97!ryY;S$rUqwRVQ(Mkj>hhU%%@^)*gSSPW7gbZ|h2ON(52r9ee;3Qcx z>M*bLEC@q3U8(yLc<$VvWz9dL38$l6gQgdjsSv>^> z@|uZW#Bj)>eF~6rX*puZs<><9h22>&W-NfUve|C7R`<~S! zI`Y$}mTeydrA^$e3r8gam=nn(gzT1z&Q1@bk5*brl_&0+z!;Cn!hMEiDf{!X@rP&G znh`&RSil39;lBNTTo$jGtm$SYzb&sfopT6)KNlf9zG&uUa^Vdl<6@0DuLqU{>`PGv zqO0v1r$wSvkBxh6k~IU~J91~wMN!sIA;G=4j3%epH$`@Imy8QT6(6RBajM>!U3s-1 z6;Qi!gfg0-^^T@$r&UzJN!y+AwR(FbaYHhI)~?T%g7gix77^7t-KK zx4i)3;N$!Jk8lYd6gX*#+CU#UOb#Q8~ELOTM zbn7CwTgqAJ8KaC}En#dEPt@wBtt&ge$qVc+ zh)-1gkcg;R&PAa`<+>E-p!Y#d;(K6*Yc`p|%}^oC>ZGxcM?k8rWI0?INfAxQ42ROX z5b~=OGgj^5@QoP$!%B+bYD79FC`tdwZ8*TgY+Cdt0V=RmdKDVI`~XOi2ifXyYnl;L4)FXhXf~eo4OARy?rmu(|#+3g-h#z!D=7 zQP@X2oAPU*TLd9VeKyU%ufFMHvn%nao-2}j*;!~tivUbFa=wl|f7K|-3whJ+x^iS5 zN5=goVC7&=&|ykLYBtfgJU>eR6qA6Y}+rx>nSZ%4|AyxGw%c{+NK$!>7V>`*=V=@-bk(taR7@7SIzj!b2 zJJyYap>E}JGLe9!7;5<;^rHQ=vge!yKCo%Bg?EJQ>(wr7RtXAuK0z0!R*3dSEtf|*o;$Xr zzq0YJyjQa)AGlsKe8%c+!u@O=VlLErWtPl_J#}xN`DAc*bE~aDJBuu!kATQ zxxtS6-af{IOj_I0K$!72(X$vJnN_1!$__*6&F@4Hu8(ilaQ~VvRRjJC^)eg1m$KX; zeAP-BpQ1q8zZfi^SesG zPr6gV5yuaC&7^Kn)uSxi`wpDedx+Q}G6mB-?e2XG$7oRTvY-xbrYm^e2v*PceV7*f z_sG==MxoLxY{6ofB|d6%FdZn^qw~V>ZVg$T3s6z)w9&iJTRf+lL{La7`(j!ObI$IA zKglMydglNh+_J_IpT%6I1*hHA0 z%#+uQyAf{+Bt=8llpO1QaLu>RflaHV=tv(hrmkTlJAOZyqYfUfOTGiAJ8U&O4MmTU zOpSV-4MkiJVv@^7vU$led|&igt00R-Wq-o6yBc;jg2bJlhz?FxvFF^{2(>j#{-I6U zC*?`eGCs^R6F^L2-QdEK=j`%?Bz)-v%)~q)u7*5gMb+rCNuPL-%u-p?PzeSQ$kX4S(gfTl;VMjm>4FDEveT(((s?WX{tpPw2Wy^Fu zxHvoHq*f!k`c>c2L6!18`J+$X{L&E4jht!Z&Ml6TBm`n_##T6di#TybP$lplOi&1` zHi7iL%=l?;*20ATJ0^hR{&@Vk1BDV9PVn*u{1@6Loe2wyk$vBxCbT)*+;+3ca71&V zutaL-d?6__ao7;|doNY#E5FF`m|v23K~}PC_)J6fkUNvG za2OjhsYJ4vo?dIp>eY|Ibx}lwa??Hx0}P*OAv)ZX>|TqIXjEpFUg)N_{x?O%&`9Et z>tx~6cD#|h`{6qDSK{Hf%6U>HJ^E)2`NB!ZY!B>elF@NMTJ2nG^eX1PO7Q+qbLeE_ zO922_s5pkxuAbrGPzR*@4SB+V)dk(qw4(6bS4{)xoDW37$^^2iayjcc+yrI#PSVL=$>1+2hmmcv2`j>)r$+D zOuUWG=KtthwLNEPsaA@lzT}c+GbI;L6bv}d)NT~4smgaH=RWL#t$WwAnb!J5q#_$8 zcNCN)Fk!R(-TP)S_^cW6l@$3AB**b(l(sf*u$67qmLHk+_=44l=82XVbE94y*0_4ow9VLJWx>Q5^g4^jhiKxD zNB|q=ZRxp$)8c=dZA3rufu8KPSwp2^w z-nRkAZ}t2$B9InYb<{-TXaKO=nOh1Z^cn2Z&t_aAKNh7)#Z@lILHrVs2y`1-p=mfV zOqxcZZSewT@>D>%gFH(!*(lqklqiUm>;ynO(C`w=cYUsusqT~569s>)F z=NT?yMrw4bnRAlHo!>4;#G}&YHBY9e!@A)3+MmO;XO*g`*03Jhj7^V|U=t^pb5!a* zrcpFWgLOvrtMjcX5ZoB2X9WPO@3>VG9(_Ci=8bs-LuLyZANv;6_I%9ZcY18h;k%h z&=+IteHL)L`ubk6^9}4pYWtUajc#Oj{r@QKEx@9Vmi}=NL6B1u3`#(dk_HLsmPQaHrMvXE=zH%~xc7bU|M{cOC=G-9C;Q~PM`%XoK#3}EBmW>6CBYVR#~|Y479mU>tv>6bOr< zdAU2i{kCYu%4p=Xie0N~q+rA?b#^K9JGNst)!HbE*eVa8jFlpUCx}H$VO&tdRLXt4 zFN`hE49Y#;y4E%6ycg5ye@Bdg&rQ^AR|!{REVNT0cxL%cVTFb1Ra$?Ei)i2aq(xbp zH%Wk<1lyRlhRf4}eC4Brvq$Q_kzZ@`{SY};z7Z;eQO2HdV_nIL{n|r{g_!Yz%9UU=5EqnV7u zID=SNrx+se`w#JFDZ|jb#^_8iq+WSR%SZ0^%ff99{Fj3s-R+uwm#YD$SKl9m(n;vrthn;FyhY zlJP-+dYjk6Oln%X|f(j%ip`I}7@*ATxG0h_^DhrSt(URD>qrLu|BX(gK3qLv5dMqrKE^ zb4~LN1NslZog;rO)POO5SBRPuGiA~UDZihm`+%={;NCK!8f#uL_Q$4YTivLuZ<^CW}{oOp>;Pr5iV}#agd>NPlE-A-($OBU$qCA?ebJp|Lu3Y-5=Q65B~Nb#4cIj!sp*Guq+` zmd3|aNM{v0Y`){5xSpyq9A_ngK-(W1mO@!BlF%l`U{g~VeN6K7i19U>kiUk=`Iy53 zT6WnT8>s_H+}i@ljMNp>D3PVyDPM?gcobQW`G_e|I8RglP;)c$kPT$(@D^ou6mIw5$!Xac8kJ^Ba(;eUXH5(#Y2xJ zwF>u6n*<*_TqSNx6j+ds>5|gcM3CAysN=adi_iTaIU?T0wKWA(TV?N_pd8F;Kaic| zX$_@1?UCS3JPG`C!IX|CjZup0;BkNCe$u-{=Wc;`KB*u}<`_SLDA6P-d6grk=N0_& zp-}n2X%`-T|4EiJt(*k>XRWGB2%bzsAa6fieJLuP0bs?kUO)zYCLfL0zp8Eh!gXQ@ zE`2LVb-T7ntE8D^X=X((nn=e=AF*eIv@zS=9x9)tMr!C=S^N|iYHLsSe zzGr1=C0*I~6jAs*>FskLJ#;xYg~&0T#QJ&(3v{)3o3Up`lXqa!ZqHRP?#dG{>D;)t z&l?T5zE!L(qRWbB52XqEi679;7b|@9nQpPW7pHSjW`B>aukFNLn&0VW?1@0fkdkO7 zG|kp^XO1ZRplA!Gflt}~^cERQrica)0}sYFF4ehvw;{*}x2;;kDgHx!!BXfT(|g8q zCrxe@0><4JXm}5lg3}&ikVTReD`$Tu>bQ$W7}TR}%$1|2@Hj#*+HR<fWXr%pmRTo&)&77O&~!q1JJ0wEHr*dG-8 zV)B*R1e#K~gql|3_ARD5zFUZFzk9{`e7xTc_T7h|ArVEt_^tA@MHJ{bvLsfW=XM6| ztsRdb#WcQjrTIafXZbXiLLn`jAtmxTyQDT-vED_w?-YZi=a_E zppf!Mo@dfK(+9I;<}f_F(RP4y(}GGXjEgXlZkQ@pO4=#&o>*l8t(fuXWW}ND#8U~6 zUG_D`hgyOn=yL9DlTD@l$|33aUVLyb&JS|{Y#`iXdT`JM6NZrV+%Dg*L^kk!?E zVMqBezUWN6+*F>TT$XhawTJcg7G~y6G^wc+8E)(%XXc-M+L~AZeKl;7<6LKyKM7e> zWuQt8ue@9yGzAvNs+D~YChRW?qIuo$C?IT&Y+fuF*4kM+vc)bi#?2sP_PJ7Y8i;jo zg-@TubM>ufVJiF{1e**fvn(pg;4$XwM5Q*zq$@2MsrmJxu3OaGBRnF4Y372;SKGup zce~Zl^{5ORWI6}v8x)1)$Lf!k8<|)f%qlC?Fo&ms2K`J}TAUWc%xA)H8prI#*9tB0 zUn_~u7g8BakW=D@Ruh9X+WB`hYGR-h@@3(+90w~oD&KB|B`>mP~BvG(X6OS2M4?nAK(^izCa&3Zm5o~soV3s>tOA*P1O~>ojJ6KO0}-hY25cU zR)h14q01dgxQoYONS9{?k*MGp0oM+%bU9CHa*~GI~UEsMAYU5 zp!hr2`s@9q%E8f6x8;FTH=}9w)MzJZ5N#p zw|)J|NhwNxKlk>OY7=>mTEH}$2=3GyTBEXeK3R*o&4#11A@UeS)%3-p{Oc<@mT0>z zg9QWFbSGI~R#~0Si~gb z4x5QAbOK6_PlcP3=?uaqIKCx?ZC2eM{u$w3mhPUN(66Z@?fmncpHQ7ueD7Jpns7N~ zR{;Z?Z9k{j`+c4L^mc8$s>;2KQ6mkH9ElA=ivowRQe0AUKVT5ZA(Nfs<&O*7JdIeJ zaP}oaU&kPbS492>ed|UQzhHH>iy~eaobkTEG3lEWj1J9Jmxr~CXe;C+@*5qVA)@RP z&q#>W{R$s!Zkr}Eygi%D44qYr@O-kXK?~q6Ii=<_>GAdbHjt_xYl%A{K6X>Z;}%M_fwvmbTT@MqOOXr^QTR+FZFlLgI14rDw8NPEDGuwY6{o+o5Y5w6VpW8>3Z-KN=!Me3{^*rDceLh;T_I2M_(5gjp}FK zQANrtd|#UJR`1G04_H|h6XmkKVXvxC@&E;rkMN5_7pw>M#CGV8?I2YMMuolmhU;sX zaeIZ$W**m5zNMk%w8r%n^}`S3W<(Tu*)w`gmSAfOZ89C#pUUjIX|fYdFE{Vh;;*uE zz=b2D`L*ahg30b>eY?pPOJ>aIpCrGiti+)#GSt}l@Lcy+NRt+`+dMwNl%dD#3HaQ; zCkM#F+7Pjp3tneGOTd(};wOg5JNH;BIx~Lk`1WxBUjCz`)z4s*A=8ad`r}WS!4s!j zB#N3=Z5fyyx0!igzgW*hht7_lC{#T(R8DAtwMN&y)lY5VCtd*u3EObYLxlVigmKjg$0DRHR9}gfA8{jE zF zoFx)0W}FEPGcSTY}-DOrX@|I*$gjd+#T*g+m<3te1dh_JU(=*H*lP1YBdVuV+&8aDYZ9y`3r61 zNs$#|s<9Ara&h02)L_J@T>M`px>(#CLu(Csgj)|oP5}!#Hm#AL(kD`@<%T&} z%EwJiZ);h2iFOfbt>yMpqC#)1faD}+L3HY1%QW1c-guYUOXXgA@u zbmY6z&m==bZq93j=ip6kwl7h-q)$d#BaGm#TV=*In^yP?=sUNtepE)9?e-;%a+^!* zu{?+$n`Npiz(SAG$4q@i`%P`Hc$ldow|(ZuW54>L`X1jXd%Glay)8qt)_`#pAzD2$ zERW3B12&|^2C)VgHEd~_o(bLkKNrK;PQ@_&(iM#=gY~}O#3}LV0=eDYo!}-gil2!$ z5wnwuqUFUXY^5Gno*Mc;Cv%`#5|(tLGMJE#cZkaYE4s>OgdlI*Powx z%7MG1wHe;UM>T;?B|nU&Z?HoRIBE7aS533N|BxLaXW7ML8r1F=-u&o8Ufh&V7DOB< zHJD{!U+dpqVFa#MGPm)xagVVLmEZ6b(;c&b@LLvl4S$kiM2x6Rx{&H5XuUjTix-t# zbaH438K~ydBXd&?S14%>4Zfhp?059ecqN}bd2go(-+XpudoM}e{pBiuV!SxEX6B6y znWt8jKMJ3gQL=?M9~j-V@p(OWq{%Cuq&DxSTpq<;^B_i^ZgCqpJ;pVWc_N@=EoG=b z%A-o_!jex(9Iy2y;KMxz9PSZ-x0TQB99;)^nHK#ye;E>uy%;{JEi%E&r7YX%WH@4P{>t;KdR2w4KH%)Q7y*`(|%w* z2o|ppC>>`~<(*1`6GMTDe0r_M$CoFxvU?=MOGRt;wlmK|^D(j%Zp9%dyr!Ifn3a?K9z!VN|G}loowvsmYp|s7l+L zWzyb0sa=qY?GN;SZb`!AFMr0`?R?=)T+BvbNT11)6QvU}Y4R!Jg5Qu>P_pg`YI)@| z+r%4aWsIB5dk|EPyjyA>0DG30-6zamDw99R57}$akyZ7as^kmaDzLw4VF>9~Vv8=% zK1FGYzFp)o@8(Sw1^qc4zL&jz)Vb-*^7e@Tj_Q!=55;Qx;yL389(e!z;psAk$Bxp; z-pq9wTQGqYXBBF$p!X+vvs5B%^%rN~@^5W$fW6k&CD8n+Bkd)P*DNum;+{SC6}lIl zgiZjGhiK8=Kot)m8;6ZRZI0+-IUO^%H!{EGkh;ymS<`^LA!pCMD;c1vovItU;~yN; zEyDdR(wtA_cSx-LpL<3_v7)S)Rt=&A5d)wS9OAuEMSl74yT{_(_b54(ccr`*=`?RI zlk=aBTPd;$+t=Ok!~W5^d6PhDyh5umvpRf%Z}v`AOrbZ^rleoz4&l+sgL{4Jt`Uu* z)E1MWB^@V|x7m{`XaMioU{Ilc{^VzH=Z*#6#CBzxa66)%m6D=ULDl1_5VRE0WBr^0 zwVgw;a~=%$8L_vuXS2aKGV(GZ1j-!TGmD6nx-#V3oO-l*p%=`1;L7tQct-AcqJ2$M z6brY0)tfP~F)dPKO!tHvM2J+ZsJdmtnXY%nU;`X1<9{DH%pb zt|yhDv;5Wzvj`ETgblWo!oKtfGn0d#?k6v1-HqV{mW42r!YP|`U!EVb)|ouPuU#5V z+NJ$nsY0ZFcqYHj5~gc^{<8F7R)lFE`Gh*c>J86Ko`k>~D~-E}hmuIx>gMyHC$rn8 zy9>94$emS_4heAg&YE=n=awDgXp10v-$qeYwQS%NEbIOAnU;l*Gvj?9Q>{I*J!RVW zoEjqdHZqAx&ACCJ`R46n`X|=hj&1FfTWp;k`p}$t4txEXbO5&~ebr%-#_hYs)E_Q} z6ZAd1hhAx&CNF)f&401|`OGL9Bl`T|cegncGw)71z|X+4aX8&B6P1=^sN`cWujhcy zoCaofV;2Wtk@ege*I-q))Q#-M{)s^~<=tjMQCz}ebsb#Ky&ZQ>^TdURrgG25G>D^3 zN4s^Z1wtQaC$k-!PFy%2Hl`oD=qQ>Ec=g4gK2|f|LEJoY_0v}6E0?auT>D^Xjgf0e z)v)?PzA(dp`isB~6@nbZ!BS0vWoDSB(2Lq1eU-@@*mpkP3Xz>H#cyj51mhTM?taP9 zfupVIY`{E(-n5r;j+fspujW-hBS0TKgsXjOL0P$9Q$0bk%oz+FuxzHuI8tC_y_MNA zQm$>>ed?Px0+8TBQ~2CcCFJ8kA9dt~fAkodqN!~ss~{>4?M~On$?mz zjG21=RpN6Y49UskR=`p{6AIC~TO}#q3Kh=&6Jp<$F9reg)diyhy{efGCmO zj)65hq3Cc?R{rt1?r!kZ8>Pa!Esl+L@?d(>J*fyyr#7K>W-L!A`%Q^_#^=;_pzj+a z9hv2yG3)2NXxDR8gtgkGzl`I;ILWYc@w}Bk4+=d`T~Ga9)Rcw_v3{UwsPS1M;LAmy z?W2s?_|3?Iez~OY8#Lw#PL6>Ix+B_AH3eNN7mQQ*F$AB`>hd$@m*tw*6a8O2&rUe$ zUA!-LIhVHB zvVV$>eAqQ|`x*C7f#jZGOooiC9%a0`Dx;o6j(%xd8DPIzBZ2hlxpTfIlPjZ>1sPAu znr@f?1+($o&L{q)yE67;M2?QF5LSeu_u$Qq(#bfA%{XXMV| zw&||!O3KtQHkO0Pt@waq4Jod)wS3L}TnCBd(U592Tl^aD{^xAOCtWyc`^P2FJOSTa z^$ye9kEtuRL6S*!nbVPOI&L?F;{7&p{Oo_cv&<(IAhOU9FG&r@BUq2z>7LtEwu7b9 zZ!khU;$L}1*BguxoaWL-7FG|+cqf~0y~pW{`m~iaAM5yr(6uw?gup#TDqDef+t_{B zR$pYI;zfIrdf%!i;>gTJY6KfIVclE{%UEz6Yi-2))9PqMLkoA19NmfDth|NY7uga= z5syXd2qES*HWwn(k8Shtvb~cXvAgF_-q9@);g&O?4@3iR2H9yRVk~LPec7XU$c65!cp3`yB;p9l^sjccZAoZ@%1Yhz{J0SlUk*8^ z%wGzX>2lF8ej{E0%=-#3tC|sVL9nAu;;4(qg=^~t6n#q6HD2c@%(NLv)t3+N$PoaF zM=Ob}9<{0^d&T*TYwz7cmQSebI4%hY2rM2+mP)gwKVRO%i_rB1$GjI`NXJ+b`BuuO z9VD8AT2h_xHGB_|&-jj}8V9_*DDvrp@{GrO-HCo7OeX)Tz-N(dp*)rgy)^}JuNv!> zj655C1d2qR_t@6>Ts9M|zO;;0kaov*)!gK0^0TX%vz3btmob+y!ot?mW?beDZq8g5 zTo%q|2;`{Oq5f^nTwJ*9kXPvK-Rz9boL#IekPvIXK-L_&91+fD_P;p(`t+NhBT_a8 zQ?CDn`u`jL{|~@_D^IT9Gx!s*&5g^=-W18r#KGC@U)cZG(Eop3C;RIL|7AIVt2g+0 zVgJ0d4+cQE!T-E$_UG*~ZXmKk!+(3bEZIvy{pV4K)%RB%GFY%PLCEK4BvBP_$a?y+^CO5zoPgt43ck1c0Lj{ zMkN%$dd!qyhQh-^QvG8gnAwqPBb>=Io67WAmU0v7W4ar#K}9l3!sAdn{WN4P)6?$n z!p=XZzJ)M(s*0l;6UBc;k;p-#4rN5cwUlCdhnk^^t)Q~|lXmTBQI~<^Gs@~A{Y5|L zMJ&zgljek<6puVynDGbEg=w||ndbcO@<{z;RHWGmr2TNQIm$>^N|t!9?{*{xAmBML zLzj%E{4xHMFlUS~bYzqzk9IlMM|!0{Rjg|ONj%>Y4&aN9BcsF@asSD@9=nox z=OTr5gVx9w#U|_y`iO|4wgK#K!o}vPfbcF!7M2^*H&A8FbKYWv5TO{d1UB<# z`Jt5Gl>lmEgfgqfpd@A-qs4gM7>-50M{z^)2k=6I6lJ>?iTnQu#VFJe4=Zr6F$9!emn5x@vJBvr>~vj0hEw$pTM7?&_{L6fNU3eU`8yHWa47n)HEmo)3r z3AU;tUVum!B;kjIjx1HJG+e&K?j@9XTq^0#RMSpfFCENtw@KQnrm6ZZS9u(0{%FnJzJ}XcI#_?sh9qk z0K=QE`-hRLyxOEzq$l-_nFmn?N07}?-MhPCa^!*!_sb0B>kq!ZZ8I+r^lH{OVm3W# z?Ov-g)FjHoxs_XkO?cmB3Jjf0p`i%30tgN$rR7?e9TwQiVr!_E4al~=coLcv`6<28 zAKdm5C@SbXbY}Y1??va2IFuB}C%TYP`Bz^7X^;%<9Rk~EXQw`xR*uK#;2Z(XRy8I)yQs}eXf*Wo5iAY#u4&tzRZab zdwVBGPwndk$Svw96uSa**e6$@%6JIZv&SMHdnlHj_ZuI2+}@+b&>w~(w<`&zs}kJH z?0B~{xV40m+!W#y?6|5Nv6j!?(;X5Kb6a$Mw~r#=&OFZ#E*&>7-6-{Bw+Pe;UbI`b zdaWqLRovRIoKoNIEQQ)*dB!J|pIcw4GJBrxCEs${s8NWKP;u&OLn$nd z$C#>ISxxqD=L^UFTKD3E0!*-D;Y*1njVrM1elC4gsJx+)M@(iV=J27a)fCG*f<~Yq z4uOJm^Akq&>wW~jv!z=6R|mE@7xS~%soEHsc@40H3yRX5{$LSPJZkHPI*nTveAfb;u}x=!W?HP&^Plv@dOM2=6zN z@4wfYq#@6!C3YcOFt7Rc)kN!Ezufxb+POq+lr8;@~C zdsnQDP+QpS;UM|mxVseYQ|cek!}+L}19}g{76Py)p8`J^$P9m1r)hBb#7-`Gd&fcs z=VXe@{|WjRU|?rGnIb`C3Qn1KS4PuCf~Fmnczw+7);ZZL4t4x(0epU~n7Qs8|7Uh= z71SvijHUX$Q5XKUzTbGCcRXQb@j#;(I*UuKUvIWURa&3sAKy5Abj~7IB6s86*AZIA zI~6q1E_?J_x-p3jpC1sntL&#N*#j~44EA+J3xprE{tV-7_O3pROzX225s`-JzlSid6)3-nXnpAq?YnE5JeE$8idU=iX{NcnkiB6JFki+(o&`4NpLH{y zOn`XSq#k~P%*6I;ui){XM-2*S%v=%iR;5F&gznXysX4@$0N9h1;8`vHiEamy8Zx9`|CrHzVz1JRV&)dq`Hn z;cESa%IWQ(YvX(VEWOFR!XNJ{b4d9q%&T9$JBhgOl7>&6jo;)|6!_9Mkap%wlk;&T z-b&6+;e*FaktLJfLGNW~cNtc5RwqFQ@r5f#a^D1%QPo&-+STaK+tD?WdPj%jU|&_C zIUKVLy)vD0igOCHo3W($CW4t9hg*1->ys;H_|%-Q!X_yzqZc@8i^xeYaJo4@}k}8IP+J z!)buaJpdrV2aAKPBh2S68)-!!jGf!*?PB??3Se$)x*T!RaZ)w=rm*Q zCY5xd#26fsn&HY6$q6B_ekCA!Q~z=KOJnv(foW^9XCzizf;XLy>5bGc zjJVHh?{)CpZ6W>4VM4c;TqQBvXLx^66Pp-+Wya;?y_!#ruJLx##lz0e=x*f$2X}m# zG$zto1E<4^_Ch|3tz%24Jgl%1-efDsZOf`MyLj>5Ke1MujG%Z*P)JS824F=1szXtgfn0^?{29~frX1m3-jWITPP$fbb#W$Ib@oe`1Jjr?HBUKC|u9u_{+ z%S9>=3`OGVJ$aYCLa&thQI;gOT1W+^8CkEi$gS@ZOZ1dlBA<=bmV619o7F{Qg$Je{ zQ7hBa+Yu-k>Y*|v{J?;4*YvyKQ8chZFU;>t$>|-UY1lCr7blx0_ z)CPL=Czq-*R?e1^wjO;n&pWY^hmN#gd4_okmbP9~Idj^8XlivZC;5q7-N$+@3GWb? ztk8nre{g6{(;9E+4+pB!ykn_*+?!O{RgkXWC~NQu-K^}WLrz}R4>tjUAG?r#SmXBg zy{V98iI$$^$5vU%Jq(&zGB>JXqVF{^$CBoa4$}Os3jr~OO8%T928)aC?tRTX``#(b zJ-L{o3u|4kezMm^Jvi(O-tacS67DLo<7>Q7|GJ4RVV&xxty0`4|Ednt@$Mc;Bh7}0 zMG<6Nhsy1zDko(V_WDY6iez(Fl01FB6^1x&?8B5Vw@s({C;2{Q%oW|=$l>d&FyWt# zsGmM&(0S3rNRp}iQTFhun*F`>3wZ7%(4C99ki*+`%6`hu`}>N$Ny!tbsn9xMhI9&? znbghotlS=|EUvP-MG=?RK6Qbf7X9H<1@TLR>FnEMso_+_v^kJGy+PcSY7X-X(*2f( zHO2-F(ePfAg>k7)*JcyLJgHA|?x40U-~jjd|kB>F-M0vNwP) zD`HZ4x*v!Zd9djc&^~r}6C#?-a9&p@uX(66_ZO2(wjW3XQ;thn7lo?NXOW z-iHwo-5)Y;e6wSvbDqZO^Qd{v-(R2MwxIZlV;pDVgY3@fp12=O&&GSd>*wg-g!Nji zb_rE=Kz8WZ$(`KU6qWi_d-)jO#Zt@Z@o79-z>GxG{KOL2mf_uX;3s}DZPL(f=oKoe z;Ke7q*Bvvn0jb!8RW$mYxo)w$IV1@^HHrs!^y_|V#51$D;S<;`FIY%1R4nRU z#n;9bW9>GeK*v557t)-@&nMDfnxH_~Qwtq00y)K#C`$Qxd8N!=&k@A!Eu zH@dU<-(;2kzm$JeRF>6|Q~I4%`VX-9m(?GU(D*!HZUB^*=l_3J35kgKA3%5z$h6R9 zmd6~K)v@|jtI>fAnX37nmH8hOL@uwtP64g=Fxh%$sl;zSIS7fXCWQAzkouoh;j)z`j;rM%i^8?be2C2^txQSFK>*PNFd`ln+4a9-H&rvH)!biL$=GQz^l1({!W za6^U@W(RZqVOE=rwN8|AQNH;rx3=LVt6E{T>*0ZQ$Q}|6k^DY2M#9`DIL`etsDW@N536 z$UW|I8h;Kc(e>e*8Qu+Vrr0ncq>Ski*3}A6IHCH#& zz7OK$2694>TbbD9(+lKA!-Ay#<14b%usHIO27pDJ4-5o>km-F02n>XC!`XpgW+0Fm z*-pX1^#5w2?u>ABG&2QoNg`}r%#f0*OK5XSy4l(qBkb)jMNzl3aseR!{uT+~=3-@U z0kE|)c64xbv^8_)0BBx5G;y#8KsZ6%ARb=u|44lw%n1gtXqg#<#Q-o)9!@ALz{U}Q z+yc!I0QY}(xupN$%bw0==2$?aq99nnfBpcVJUmG6Gza_-4a9xvOP4i!@+Q*g|E=K!PjWKpzAa^=+a59^t~(>bcMzXzVxEM z+5wTItMUQCJeOP2-|av=mz($BGzb)lI(?PKbLqE#w*$kj$pYj7g0HpX<^^6K4|W-V z{vMA9`TXe`4Su7Y;?DKVGHra9`Ck5C*z# zb6&{hLF{i?kS4k|9xv>gZvf#i7&0>d)fWT;BcZ>q(m2O{d6ZbQy*JZilS2d({s>5a>1gf{+XH>fAucv;Q?6 za9^(6zj?rUxv%*j2nyx7>IWbw5BF6+0P%4Do3#bMt``{m+S&)fV3(Wi-}8mSF1Luk zX>eZXRhdD^AaSiPjQ5(2z(C;TcKY{tyu6@mbK?bGTdQE?4sw+*7|44~=F4Dzl?U?i z{55-k!JupQK=RzWbs_v0>=`PQ^ z|KQ>3jIctUHLwH(09HK|w4RSA?_cud@kqIr4I2F)~Uh HNn-s!kSPqG literal 0 HcmV?d00001 diff --git a/doc/interface_functions.tex b/doc/interface_functions.tex new file mode 100644 index 00000000..c33e457f --- /dev/null +++ b/doc/interface_functions.tex @@ -0,0 +1,1107 @@ +\documentclass[a4paper,10pt]{article} +\usepackage[utf8x]{inputenc} + +%opening +\title{Interface functions} +\author{Martijn Wehrens} + +\begin{document} + +\maketitle + +\tableofcontents + +\section{Introduction} +This document gives an overview of functions that are usefull to end-users of the enhanced Green's Functions Reaction Dynamics (eGFRD) algorithm. In other words, it lists the "interface functions". +Basically, this entails a list of function definition plus their so-called python "docstrings", obtained straight from the code. +Note that functions starting with an underscore are actually C++ functions ported to python by boost. + +\section{Functions} + +\subsection{Functions from \texttt{model.py}} + +\subsubsection{Core functions:} +As mentioned, all documentation in this document comes straight from the code. To obtain more information on how to structure a simple script, one could take a look into the sample directory. + + +The class you need to set up a particle model: +\begin{verbatim} + class ParticleModel(_gfrd.Model): + """ + """ + def __init__(self, world_size): + """Create a new ParticleModel. + + Arguments: + - world_size + the size of one side of the simulation "world". Units: + meters. + + The simulation "world" is always assumed to be a cube with + *periodic boundary conditions*, with 1 corner at [0, 0, 0] and + the corner furthest away from [0, 0, 0] being at + [world_size, world_size, world_size]. + + """ +\end{verbatim} + +\begin{verbatim} +def Species(name, D, radius=0, structure="world", drift=0): + """Define a new Species (in/on a specific Region or Surface). + + Arguments: + - name + the name of this Species. + - D + the diffusion constant for this Species in/on this + Region or Surface. Units: meters^2/second. + - radius + the radius for this Species in/on this Region or Surface. + Units: meters. + - structure + the Region or Surface in/on which this Species can exist. + Optional. If you do not specify a Structure the Species is + added to the "world". + - drift + the drift term for this ParticleType on a + CylindricalSurface (1D drift). Units: meters/second. + Optional. + + If a certain Species should be able to exist in the "world" as + well as in/on one of the previously created Regions or Surfaces, + then two distinct Species should be created. One with and one + without an explicit Structure argument. + + """ +\end{verbatim} + +Species should be added to the model: +\begin{verbatim} + + def add_reaction_rule(self, reaction_rule): + """Add a ReactionRule to the ParticleModel. + + Argument: + - reaction rule + a ReactionRule created by one of the functions + model.create_<>_reaction_rule. + + """ +\end{verbatim} + +\subsubsection{Creating regions} + +\begin{verbatim} +_gfrd.create_cuboidal_region.__doc__ = \ +"""create_cuboidal_region(id, corner, diagonal) + +Create and return a new cuboidal Region. + +Arguments: + - id + a descriptive name. + - corner + the point [x, y, z] of the cuboidal Region closest to + [0, 0, 0]. Units: [meters, meters, meters] + - diagonal + the vector [x, y, z] from the corner closest to [0, 0, 0], to + the corner furthest away from [0, 0, 0]. Units: + [meters, meters, meters] + +""" + +_gfrd.create_cylindrical_surface.__doc__ = \ +"""create_cylindrical_surface(id, corner, radius, orientation, length) + +Create and return a new cylindrical Surface. + +Arguments: + - id + a descriptive name. + - corner + the point [x, y, z] on the axis of the cylinder closest to + [0, 0, 0]. Units: [meters, meters, meters] + - radius + the radius of the cylinder. Units: meters. + - orientation + the unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] along the + axis of the cylinder. + - length + the length of the cylinder. Should be equal to the world_size. + Units: meters. + +Surfaces are not allowed to touch or overlap. + +""" + +_gfrd.create_planar_surface.__doc__ = \ +"""create_planar_surface(id, corner, unit_x, unit_y, length_x, length_y) + +Create and return a new planar Surface. + +Arguments: + - id + a descriptive name. + - corner + the point [x, y, z] on the plane closest to [0, 0, 0]. Units: + [meters, meters, meters] + - unit_x + a unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] along the + plane. + - unit_y + a unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] along the plane + and perpendicular to unit_x. + - length_x + the length of the plane along the unit vector unit_x. Should be + equal to the world_size. Units: meters. + - length_y + the length of the plane along the unit vector unit_y. Should be + equal to the world_size. Units: meters. + +Surfaces are not allowed to touch or overlap. + +""" +\end{verbatim} + +As particles, regions should be added to the model. +\begin{verbatim} + def add_structure(self, structure): + """Add a Structure (Region or Surface) to the ParticleModel. + + Arguments: + - structure + a Region or Surface created with one of the functions + model.create_<>_region or model.create_<>_surface. + + """ + assert isinstance(structure, _gfrd.Structure) + self.structures[structure.id] = structure + return structure +\end{verbatim} + +\subsubsection{Adding reaction rules to the model} + +Function set\_all\_repulsive is called automatically, but it's docstring is instructive. +\begin{verbatim} + def set_all_repulsive(self): + """Set all 'other' possible ReactionRules to be repulsive. + + By default an EGFRDSimulator will assume: + - a repulsive bimolecular reaction rule (k=0) for each + possible combination of reactants for which no + bimolecular reaction rule is specified. + + This method explicitly adds these ReactionRules to the + ParticleModel. + + """ +\end{verbatim} + +\begin{verbatim} +def create_unimolecular_reaction_rule(reactant, product, k): + """Example: A -> B. + + Arguments: + - reactant + a Species. + - product + a Species. + - k + reaction rate. Units: per second. (Rough order of magnitude: + 1e-2 /s to 1e2 /s). + + The reactant and the product should be in/on the same + Region or Surface. + + There is no distinction between an intrinsic and an overall reaction + rate for a unimolecular ReactionRule. + + A unimolecular reaction rule defines a Poissonian process. + + """ +\end{verbatim} + +\begin{verbatim} +def create_decay_reaction_rule(reactant, k): + """Example: A -> 0. + + Arguments: + - reactant + a Species. + - k + reaction rate. Units: per second. (Rough order of magnitude: + 1e-2 /s to 1e2 /s). + + There is no distinction between an intrinsic and an overall reaction + rate for a decay ReactionRule. + + A decay reaction rule defines a Poissonian process. + + """ +\end{verbatim} + +\begin{verbatim} +def create_annihilation_reaction_rule(reactant1, reactant2, ka): + """Example: A + B -> 0. + + Arguments: + - reactant1 + a Species. + - reactant2 + a Species. + - ka + intrinsic reaction rate. Units: meters^3 per second. (Rough + order of magnitude: 1e-16 m^3/s to 1e-20 m^3/s). + + The reactants should be in/on the same Region or Surface. + + ka should be an *intrinsic* reaction rate. You can convert an + overall reaction rate (kon) to an intrinsic reaction rate (ka) with + the function utils.k_a(kon, kD), but only for reaction rules in 3D. + + By default an EGFRDSimulator will assume a repulsive + bimolecular reaction rule (ka=0) for each possible combination of + reactants for which no bimolecular reaction rule is specified. + You can explicitly add these reaction rules to the model with the + method model.ParticleModel.set_all_repulsive. + + """ +\end{verbatim} + +\begin{verbatim} +def create_binding_reaction_rule(reactant1, reactant2, product, ka): + """Example: A + B -> C. + + Arguments: + - reactant1 + a Species. + - reactant2 + a Species. + - product + a Species. + - ka + intrinsic reaction rate. Units: meters^3 per second. (Rough + order of magnitude: 1e-16 m^3/s to 1e-20 m^3/s) + + The reactants and the product should be in/on the same + Region or Surface. + + A binding reaction rule always has exactly one product. + + ka should be an *intrinsic* reaction rate. You can convert an + overall reaction rate (kon) to an intrinsic reaction rate (ka) with + the function utils.k_a(kon, kD), but only for reaction rules in 3D. + + By default an EGFRDSimulator will assume a repulsive + bimolecular reaction rule (ka=0) for each possible combination of + reactants for which no bimolecular reaction rule is specified. + You can explicitly add these reaction rules to the model with the + method model.ParticleModel.set_all_repulsive. + + """ +\end{verbatim} + +\begin{verbatim} +def create_unbinding_reaction_rule(reactant, product1, product2, kd): + """Example: A -> B + C. + + Arguments: + - reactant + a Species. + - product1 + a Species. + - product2 + a Species. + - kd + intrinsic reaction rate. Units: per second. (Rough order of + magnitude: 1e-2 /s to 1e2 /s). + + The reactant and the products should be in/on the same + Region or Surface. + + An unbinding reaction rule always has exactly two products. + + kd should be an *intrinsic* reaction rate. You can convert an + overall reaction rate (koff) for this reaction rule to an intrinsic + reaction rate (kd) with the function utils.k_d(koff, kon, kD) or + utils.k_d_using_ka(koff, ka, kD). + + An unbinding reaction rule defines a Poissonian process. + + """ +\end{verbatim} + +To put this reactions into effect one should also call \texttt{add\_reaction\_rule}, e.g.: +\begin{verbatim} + r1 = model.create_binding_reaction_rule(A, B, C, kf) + m.network_rules.add_reaction_rule(r1) +\end{verbatim} + +\subsection{Functions from \texttt{gfrdbase.py}} + +\subsubsection{Core functions} + +\begin{verbatim} +def create_world(m, matrix_size=10): + """Create a world object. + + The world object keeps track of the positions of the particles + and the protective domains during an eGFRD simulation. + + Arguments: + - m + a ParticleModel previously created with model.ParticleModel. + - matrix_size + the number of cells in the MatrixSpace along the x, y and z + axis. Leave it to the default number if you don't know what + to put here. + + The simulation cube "world" is divided into (matrix_size x matrix_size + x matrix_size) cells. Together these cells form a MatrixSpace. The + MatrixSpace keeps track in which cell every particle and protective + domain is at a certain point in time. To find the neigherest + neighbours of particle, only objects in the same cell and the 26 + (3x3x3 - 1) neighbouring cells (the simulation cube has periodic + boundary conditions) have to be taken into account. + + The matrix_size limits the size of the protective domains. If you + have fewer particles, you want a smaller matrix_size, such that the + protective domains and thus the eGFRD timesteps can be larger. If + you have more particles, you want a larger matrix_size, such that + finding the neigherest neighbours is faster. + + Example. In samples/dimer/dimer.py a matrix_size of + (N * 6) ** (1. / 3.) is used, where N is the average number of + particles in the world. + + """ +\end{verbatim} + + +\subsubsection{Handling objects} + +\begin{verbatim} +def get_closest_surface(world, pos, ignore): + """Return + - closest surface + - distance to closest surface + + We can not use matrix_space, it would miss a surface if the + origin of the surface would not be in the same or neighboring + cells as pos.""" +\end{verbatim} + +\begin{verbatim} +def get_closest_surface_within_radius(world, pos, radius, ignore): + """Return: + - surface within radius or None + - closest surface (regardless of radius) + - distance to closest surface""" +\end{verbatim} + +\subsubsection{Adding particles} +\begin{verbatim} +functions +def throw_in_particles(world, sid, n): + """Add n particles of a certain Species to the specified world. + + Arguments: + - sid + a Species previously created with the function + model.Species. + - n + the number of particles to add. + + Make sure to first add the Species to the model with the method + model.ParticleModel.add_species_type. + + """ +\end{verbatim} + +\begin{verbatim} +def place_particle(world, sid, position): + """Place a particle of a certain Species at a specific position in + the specified world. + + Arguments: + - sid + a Species previously created with the function + model.Species. + - position + a position vector [x, y, z]. Units: [meters, meters, meters]. + + Make sure to first add the Species to the model with the method + model.ParticleModel.add_species_type. + + """ +\end{verbatim} + +\subsection{Functions from \texttt{egfrd.py}} + +\subsubsection{Core functions} +\begin{verbatim} +class EGFRDSimulator(ParticleSimulatorBase): + """ + """ + def __init__(self, world, rng=myrandom.rng, network_rules=None): + """Create a new EGFRDSimulator. + + Arguments: + - world + a world object created with the function + gfrdbase.create_world. + - rng + a random number generator. By default myrandom.rng is + used, which uses Mersenne Twister from the GSL library. + You can set the seed of it with the function + myrandom.seed. + - network_rules + you don't need to use this, for backward compatibility only. + + """ +\end{verbatim} + +\begin{verbatim} + def step(self): + """Execute one eGFRD step. + + """ +\end{verbatim} +(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) + +\begin{verbatim} + def stop(self, t): + """Synchronize all particles at time t. + + With eGFRD, particle positions are normally updated + asynchronously. This method bursts all protective domains and + assigns a position to each particle. + + Arguments: + - t + the time at which to synchronize the particles. Usually + you will want to use the current time of the simulator: + EGFRDSimulator.t. + + This method is called stop because it is usually called at the + end of a simulation. It is possible to call this method at an + earlier time. For example the Logger module does this, because + it needs to know the positions of the particles at each log + step. + + """ +\end{verbatim} +(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) + +\subsubsection{Simulator time (manipulation) functions} + +\begin{verbatim} + def get_next_time(self): + """ + Returns the time it will be when the next egfrd timestep + is completed. + """ +\end{verbatim} +(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) + + +\begin{verbatim} + def reset(self): + """ + This function resets the "records" of the simulator. This means + the simulator time is reset, the step counter is reset, events + are reset, etc. + Can be for example usefull when users want to "stirr" the + simulation before starting the "real experiment". + """ +\end{verbatim} +(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) + +\subsubsection{Get data} +\begin{verbatim} + def print_report(self, out=None): + """Print various statistics about the simulation. + + Arguments: + - None + + """ +\end{verbatim} + +\subsubsection{Additional functions} +The class \texttt{EGFRDSimulator} contains several functions which might be usefull to eGFRD users in very specific cases. Because of their specific nature, they don't contain docstrings. +\begin{verbatim} + def get_matrix_cell_size(self): + return self.containers[0].cell_size + + def set_user_max_shell_size(self, size): + self.user_max_shell_size = size + + def get_user_max_shell_size(self): + return self.user_max_shell_size + + def get_max_shell_size(self): + return min(self.get_matrix_cell_size() * .5 / SAFETY, + self.user_max_shell_size) +\end{verbatim} + +\subsection{Functions from \texttt{dumper.py}} + +\subsubsection{Getting information on species/particles} +\begin{verbatim} +def get_species(sim): + """Return an iterator over the Species in the simulator. + + Arguments: + - sim + an EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def dump_species(sim): + """Return a string containing the Species in the simulator. + + Arguments: + - sim + an EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def get_species_names(sim): + """Return an iterator over the names of the Species in the + simulator. + + Arguments: + - sim + an EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def dump_species_names(sim): + """Return a string containing the names of the Species in the + simulator. + + Arguments: + - sim + an EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def _get_species_type_by_name(sim, name): + """ Return the type of a species with a certain name + + Arguments: + - sim + an EGFRDSimulator + - name + species name + """ +\end{verbatim} + +\begin{verbatim} +def _get_particles_by_sid(sim, sid): + """ Return a generator (using "yield") to loop over (pid, particle). + + Arguments: + - sim + an EGFRDSimulator + - sid + ID of a species + + E.g.: + + myparticles = _get_particles_by_sid(sim, sid) + + for mypid, myparticle in myparticles: + print str(str(mypid), str(myparticle)) + """ +\end{verbatim} + +\begin{verbatim} +def get_particles(sim, identifier=None): + """Return an iterator over the + (particle identifier, particle)-pairs in the simulator. + + Arguments: + - sim + an EGFRDSimulator. + - identifier + a Species or the name of a Species. If none is specified, + all (particle identifier, particle)-pairs will be returned. + + """ +\end{verbatim} + +\begin{verbatim} +def dump_particles(sim, identifier=None): + """Return a string containing the + (particle identifier, particle)-pairs in the simulator. + + Arguments: + - sim + an EGFRDSimulator. + - identifier + a Species or the name of a Species. If none is specified, + all (particle identifier, particle)-pairs will be returned. + + """ +\end{verbatim} + +\begin{verbatim} +def _get_number_of_particles_by_sid(sim, sid): + """ + Returns the number of particles of a certain species. + + Arguments: + - sim + an EGFRDSimulator. + - sid + ID of a species + + """ +\end{verbatim} + +\begin{verbatim} +def get_number_of_particles(sim, identifier=None): + """Return the number of particles of a certain Species in the + simulator. + + Arguments: + - sim + either an EGFRDSimulator or a GillespieSimulator. + - identifier + a Species. Optional. If none is specified, a list of + (Species name, number of particles)-pairs will be returned. + + """ +\end{verbatim} + +\begin{verbatim} +def dump_number_of_particles(sim, identifier=None): + """Return a string containing the number of particles of a certain + Species in the simulator. + + Arguments: + - sim + either an EGFRDSimulator or a GillespieSimulator. + - identifier + a Species. Optional. If none is specified, + a string of (Species name, number of particles)-pairs will + be returned. + + """ +\end{verbatim} + +\subsubsection{Get information on domains} + +\begin{verbatim} +def get_domains(egfrdsim): + """Return an iterator over the protective domains in the simulator. + + Arguments: + - egfrdsim + an EGFRDSimulator + """ +\end{verbatim} + +\begin{verbatim} +def dump_domains(egfrdsim): + """Return an string containing the protective domains in the + simulator. + + """ +\end{verbatim} + +subsubsection{Get information on reaction rules} + +\begin{verbatim} +def get_reaction_rules(model_or_simulator): + """Return three lists with all the reaction rules defined in the + ParticleModel or EGFRDSimulator. + + The three lists are: + - reaction rules of only one reactant. + - reaction rules between two reactants with a reaction rate + larger than 0. + - repulsive reaction rules between two reactants with a + reaction rate equal to 0. + + Arguments: + - model_or_simulator + a ParticleModel or EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def _dump_reaction_rule(model, reaction_rule): + """Helper. Return ReactionRule as string. + + ReactionRule.__str__ would be good, but we are actually getting a + ReactionRuleInfo or ReactionRuleCache object.""" +\end{verbatim} + +\begin{verbatim} +def dump_reaction_rules(model_or_simulator): + """Return a formatted string containing all the reaction rules + defined in the ParticleModel or EGFRDSimulator. + + Arguments: + - model_or_simulator + a ParticleModel or EGFRDSimulator. + + """ +\end{verbatim} + +\subsection{Functions from \texttt{utils.py}} + +This file contains functions on common mathematical objects, transformations and formulas. Some of these functions are used throughout the algorithm, and some are supplied for convenience. +For the user, none of these functions are \textit{needed} to make a simulation work, but some might come in handy. + +\subsubsection{Mathematical comparisons} +\begin{verbatim} +def feq(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a and b are equal, subject to given tolerances. + Float comparison. + + Also see numpy.allclose(). + + The (relative) tolerance must be positive and << 1.0 + + Instead of specifying an absolute tolerance, you can speciy a + typical value for a or b. The absolute tolerance is then the + relative tolerance multipied by this typical value, and will be + used when comparing a value to zero. By default, the typical + value is 1.""" + +def fgreater(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a is greater than b, subject to given tolerances. + Float comparison.""" + +def fless(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a is less than b, subject to given tolerances. + Float comparison.""" + +def fgeq(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a is greater or equal than b, subject to given + tolerances. Float comparison.""" + +def fleq(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a is less than or equal than b, subject to given + tolerances. Float comparison.""" + +\end{verbatim} + +\subsubsection{Conversions} + +As these functions only contain a single line of formula code, this line is also supplied. +\begin{verbatim} +def per_M_to_m3(rate): + """Convert a reaction rate from units 'per molar per second' to + units 'meters^3 per second'. + + """ + return rate / (1000 * N_A) + +def per_microM_to_m3(rate): + """Convert a reaction rate from units 'per micromolar per second' to + units 'meters^3 per second'. + + """ + return per_M_to_m3(rate * 1e6) + +def M_to_per_m3(molar): + """Convert a concentration from units 'molar' to units 'per + meters^3'. + + """ + return molar * (1000 * N_A) + +def microM_to_per_m3(micromolar): + """Convert a concentration from units 'micromolar' to units 'per + meters^3'. + + """ + return M_to_per_m3(micromolar / 1e6) + +def C2N(c, V): + """Calculate the number of particles in a volume 'V' (dm^3) + with a concentration 'c' (mol/dm^3). + + """ + return c * V * N_A # round() here? +\end{verbatim} + +Conversions involving rates: +\begin{verbatim} + +def k_D(Dtot, sigma): + """Calculate the 'pseudo-'reaction rate (kD) caused by diffusion. + + kD is equal to 1 divided by the time it takes for two particles to + meet each other by diffusion. It is needed when converting from + an intrinsic reaction rate to an overall reaction rates or vice + versa. + + Example: + - A + B -> C. + + Arguments: + - Dtot: + the diffusion constant of particle A plus the diffusion + constant of particle B. Units: meters^2/second. + - sigma + the radius of particle A plus the radius of particle B. + Units: meters. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + return 4.0 * numpy.pi * Dtot * sigma + +def k_a(kon, kD): + """Convert an overall reaction rate (kon) for a binding/annihilation + reaction rule to an intrinsic reaction rate (ka). + + Example: + - A + B -> C + binding reaction rule + - A + B -> 0 + annihilation reaction rule + + Arguments: + - kon + the overall reaction rate for the reaction rule. Units: + meters^3/second. + - kD + the 'pseudo-'reaction rate caused by the diffusion of + particles A and B. See the function k_D(). Units: + meters^3/second. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + if kon > kD: + raise RuntimeError, 'kon > kD.' + ka = 1. / ((1. / kon) - (1. / kD)) + return ka + +def k_d(koff, kon, kD): + """Convert an overall reaction rate (koff) for an unbinding reaction + rule to an intrinsic reaction rate (kd). + + This one is a bit tricky. We consider reaction rules with only 1 + reactant. In case there is only 1 product also, no conversion in + necessary. But when the reaction rule has 2 products, we need to + take the reverse reaction rule into account and do the proper + conversion. + + Example: + - C -> A + B + unbinding reaction rule + - A + B -> C + reverse reaction rule + + Arguments: + - koff + the overall reaction rate for the unbinding reaction rule. + Units: meters^3/second. + - kon + the overall reaction rate for the reverse reaction rule. + Units: meters^3/second. + - kD + the 'pseudo-'reaction rate caused by the diffusion of + particles A and B. See the function k_D(). Units: + meters^3/second. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + ka = k_a(kon, kD) + kd = k_d_using_ka(koff, ka, kD) + return kd + +def k_d_using_ka(koff, ka, kD): + """Convert an overall reaction rate (koff) for an unbinding reaction + rule to an intrinsic reaction rate (kd). + + Similar to the function k_d(), but expects an intrinsic rate (ka) + instead of an overall rate (kon) for the reversed reaction rule as + the second argument. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + kd = koff * (1 + float(ka) / kD) + return kd + +def k_on(ka, kD): + """Convert an intrinsic reaction rate (ka) for a binding/annihilation + reaction rule to an overall reaction rate (kon). + + The inverse of the function k_a(). + + Rarely needed. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + kon = 1. / ((1. / kD) + (1. / ka)) # m^3/s + return kon + +def k_off(kd, kon, kD): + """Convert an intrinsic reaction rate (kd) for an unbinding reaction + rule to an overall reaction rate (koff). + + The inverse of the function k_d(). + + Rarely needed. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + ka = k_a(kon, kD) + koff = k_off_using_ka(kd, ka, kD) + return koff + +def k_off_using_ka(kd, ka, kD): + """Convert an intrinsic reaction rate (kd) for an unbinding reaction + rule to an overall reaction rate (koff). + + Similar to the function k_off(), but expects an intrinsic rate + (ka) instead of an overall rate (kon) as the second argument. + + Rarely needed. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + koff = 1. / (float(ka) / (kd * kD) + (1. / kd)) + return koff +\end{verbatim} + + +\subsubsection{Some convenient functoins} + +These functions do not contain docstrings, are sometimes self-explanatory and probably not needed that often in simulations. Therefore only definitions of functions that might be of interest to the user are listed here: + +Mean arrival time: +\begin{verbatim} +def mean_arrival_time(r, D): + return (r * r) / (6.0 * D) +\end{verbatim} + +Calculating with distances: +\begin{verbatim} +def distance_sq_array_simple(position1, positions, fsize = None): + +def distance_array_simple(position1, positions, fsize = None): + +distance = _gfrd.distance + +distance_cyclic = _gfrd.distance_cyclic + +def distance_sq_array_cyclic(position1, positions, fsize): + +def distance_array_cyclic(position1, positions, fsize = 0): + +\end{verbatim} + +Some vector functions: +\begin{verbatim} +def cartesian_to_spherical(c): + +def spherical_to_cartesian(s): + +def random_unit_vector_s(): + +def random_unit_vector(): + +def random_vector(r): + +def random_vector2D(r): + +def length(a): + +def normalize(a, l=1): + +def vector_angle(a, b): + +def vector_angle_against_z_axis(b): + +def crossproduct(a, b): + +def crossproduct_against_z_axis(a): + +def rotate_vector(v, r, alpha): +\end{verbatim} + +\subsection{Notes on other files} + +\subsubsection{\texttt{bd.py}: Brownian Dynamic Simulator} +The class \texttt{BDSimulator} can be used in the same way as the EGFRDSimulator class, but performs Brownian Dynamics (BD) instead. Users who want to perform eGFRD simulations never need this. The simulator can be used for comparison of the eGFRD algorithm with Brownian Dynamics. + +\subsubsection{\texttt{gillespie.py}: Gillespie Simulator} +Similar to the BD simulator, the class \texttt{GillespieSimulatorBase} can be used for Gillespie type simulations. + +\subsubsection{\texttt{legacy.py}: Old redundant functions} +This module is called nowhere in the Python code. It contains an archive of outdated code. + +\subsubsection{\texttt{multi.py}, \texttt{pair.py}, \texttt{single.py}} +These file contain the code that handle the specific events that happen in the different categories of domains. + +\subsubsection{\texttt{myrandom.py}} +Contains a few convenient lines of code used when using random functions. + +\subsubsection{\texttt{make\_cjy\_table.py.py}, \texttt{make\_sjy\_table.py.py}} +Generate (respectively cylindrical and spherical) bessel function tables. + + +\subsection{Function from module \texttt{logger.py}} + +This module contains two loggers. One logger that logs in the hdf5 format, for obvious reasons in class \texttt{HDF5Logger}. Note that this logger requires the module h5py. The other logger gives output dictated more by the nature of the eGFRD algorithm. +The loggers are not (yet) explained in very much detail here, but both function in the same way. A logger class is made, which takes input on to which file to write, the logger can be started by \texttt{start()} and steps are logged by the function \texttt{log()}. + +\subsubsection{HDF5 logger} + +\begin{verbatim} +class HDF5Logger(object): + def __init__(self, logname, directory='data', split=False): + + def log(self, sim, time): + + def start(self, sim): +\end{verbatim} + +\subsubsection{"Normal" logger} +\begin{verbatim} +class Logger(object): + def __init__(self, logname='log', directory='data', comment=''): + + def log(self, sim, time): + + def start(self, sim): +\end{verbatim} + +(Note that not all functions contained by this class are listed, just functions deemed usefull for user interface.) + +\section{Todo} + +- \texttt{sid = identifier.id} gives the sid, but what exactly is identifier? + +\end{document} diff --git a/doc/interface_functions.tex~ b/doc/interface_functions.tex~ new file mode 100644 index 00000000..9181447f --- /dev/null +++ b/doc/interface_functions.tex~ @@ -0,0 +1,1107 @@ +\documentclass[a4paper,10pt]{article} +\usepackage[utf8x]{inputenc} + +%opening +\title{Interface functions} +\author{Martijn Wehrens} + +\begin{document} + +\maketitle + +\tableofcontents + +\section{Introduction} +This document gives an overview of functions that are usefull to end-users of the enhanced Green's Functions Reaction Dynamics (eGFRD) algorithm. In other words, it lists the "interface functions". +Basically, this entails a list of function definition plus their so-called python "docstrings", obtained straight from the code. +Note that functions starting with an underscore are actually C++ functions ported to python by boost. + +\section{Functions} + +\subsection{Functions from \texttt{model.py}} + +\subsubsection{Core functions:} +As mentioned, all documentation in this document comes straight from the code. To obtain more information on how to structure a simple script, one could take a look into the sample directory. + + +The class you need to set up a particle model: +\begin{verbatim} + class ParticleModel(_gfrd.Model): + """ + """ + def __init__(self, world_size): + """Create a new ParticleModel. + + Arguments: + - world_size + the size of one side of the simulation "world". Units: + meters. + + The simulation "world" is always assumed to be a cube with + *periodic boundary conditions*, with 1 corner at [0, 0, 0] and + the corner furthest away from [0, 0, 0] being at + [world_size, world_size, world_size]. + + """ +\end{verbatim} + +\begin{verbatim} +def Species(name, D, radius=0, structure="world", drift=0): + """Define a new Species (in/on a specific Region or Surface). + + Arguments: + - name + the name of this Species. + - D + the diffusion constant for this Species in/on this + Region or Surface. Units: meters^2/second. + - radius + the radius for this Species in/on this Region or Surface. + Units: meters. + - structure + the Region or Surface in/on which this Species can exist. + Optional. If you do not specify a Structure the Species is + added to the "world". + - drift + the drift term for this ParticleType on a + CylindricalSurface (1D drift). Units: meters/second. + Optional. + + If a certain Species should be able to exist in the "world" as + well as in/on one of the previously created Regions or Surfaces, + then two distinct Species should be created. One with and one + without an explicit Structure argument. + + """ +\end{verbatim} + +Species should be added to the model: +\begin{verbatim} + + def add_reaction_rule(self, reaction_rule): + """Add a ReactionRule to the ParticleModel. + + Argument: + - reaction rule + a ReactionRule created by one of the functions + model.create_<>_reaction_rule. + + """ +\end{verbatim} + +\subsubsection{Creating regions} + +\begin{verbatim} +_gfrd.create_cuboidal_region.__doc__ = \ +"""create_cuboidal_region(id, corner, diagonal) + +Create and return a new cuboidal Region. + +Arguments: + - id + a descriptive name. + - corner + the point [x, y, z] of the cuboidal Region closest to + [0, 0, 0]. Units: [meters, meters, meters] + - diagonal + the vector [x, y, z] from the corner closest to [0, 0, 0], to + the corner furthest away from [0, 0, 0]. Units: + [meters, meters, meters] + +""" + +_gfrd.create_cylindrical_surface.__doc__ = \ +"""create_cylindrical_surface(id, corner, radius, orientation, length) + +Create and return a new cylindrical Surface. + +Arguments: + - id + a descriptive name. + - corner + the point [x, y, z] on the axis of the cylinder closest to + [0, 0, 0]. Units: [meters, meters, meters] + - radius + the radius of the cylinder. Units: meters. + - orientation + the unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] along the + axis of the cylinder. + - length + the length of the cylinder. Should be equal to the world_size. + Units: meters. + +Surfaces are not allowed to touch or overlap. + +""" + +_gfrd.create_planar_surface.__doc__ = \ +"""create_planar_surface(id, corner, unit_x, unit_y, length_x, length_y) + +Create and return a new planar Surface. + +Arguments: + - id + a descriptive name. + - corner + the point [x, y, z] on the plane closest to [0, 0, 0]. Units: + [meters, meters, meters] + - unit_x + a unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] along the + plane. + - unit_y + a unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] along the plane + and perpendicular to unit_x. + - length_x + the length of the plane along the unit vector unit_x. Should be + equal to the world_size. Units: meters. + - length_y + the length of the plane along the unit vector unit_y. Should be + equal to the world_size. Units: meters. + +Surfaces are not allowed to touch or overlap. + +""" +\end{verbatim} + +As particles, regions should be added to the model. +\begin{verbatim} + def add_structure(self, structure): + """Add a Structure (Region or Surface) to the ParticleModel. + + Arguments: + - structure + a Region or Surface created with one of the functions + model.create_<>_region or model.create_<>_surface. + + """ + assert isinstance(structure, _gfrd.Structure) + self.structures[structure.id] = structure + return structure +\end{verbatim} + +\subsubsection{Adding reaction rules to the model} + +Function set\_all\_repulsive is called automatically, but it's docstring is instructive. +\begin{verbatim} + def set_all_repulsive(self): + """Set all 'other' possible ReactionRules to be repulsive. + + By default an EGFRDSimulator will assume: + - a repulsive bimolecular reaction rule (k=0) for each + possible combination of reactants for which no + bimolecular reaction rule is specified. + + This method explicitly adds these ReactionRules to the + ParticleModel. + + """ +\end{verbatim} + +\begin{verbatim} +def create_unimolecular_reaction_rule(reactant, product, k): + """Example: A -> B. + + Arguments: + - reactant + a Species. + - product + a Species. + - k + reaction rate. Units: per second. (Rough order of magnitude: + 1e-2 /s to 1e2 /s). + + The reactant and the product should be in/on the same + Region or Surface. + + There is no distinction between an intrinsic and an overall reaction + rate for a unimolecular ReactionRule. + + A unimolecular reaction rule defines a Poissonian process. + + """ +\end{verbatim} + +\begin{verbatim} +def create_decay_reaction_rule(reactant, k): + """Example: A -> 0. + + Arguments: + - reactant + a Species. + - k + reaction rate. Units: per second. (Rough order of magnitude: + 1e-2 /s to 1e2 /s). + + There is no distinction between an intrinsic and an overall reaction + rate for a decay ReactionRule. + + A decay reaction rule defines a Poissonian process. + + """ +\end{verbatim} + +\begin{verbatim} +def create_annihilation_reaction_rule(reactant1, reactant2, ka): + """Example: A + B -> 0. + + Arguments: + - reactant1 + a Species. + - reactant2 + a Species. + - ka + intrinsic reaction rate. Units: meters^3 per second. (Rough + order of magnitude: 1e-16 m^3/s to 1e-20 m^3/s). + + The reactants should be in/on the same Region or Surface. + + ka should be an *intrinsic* reaction rate. You can convert an + overall reaction rate (kon) to an intrinsic reaction rate (ka) with + the function utils.k_a(kon, kD), but only for reaction rules in 3D. + + By default an EGFRDSimulator will assume a repulsive + bimolecular reaction rule (ka=0) for each possible combination of + reactants for which no bimolecular reaction rule is specified. + You can explicitly add these reaction rules to the model with the + method model.ParticleModel.set_all_repulsive. + + """ +\end{verbatim} + +\begin{verbatim} +def create_binding_reaction_rule(reactant1, reactant2, product, ka): + """Example: A + B -> C. + + Arguments: + - reactant1 + a Species. + - reactant2 + a Species. + - product + a Species. + - ka + intrinsic reaction rate. Units: meters^3 per second. (Rough + order of magnitude: 1e-16 m^3/s to 1e-20 m^3/s) + + The reactants and the product should be in/on the same + Region or Surface. + + A binding reaction rule always has exactly one product. + + ka should be an *intrinsic* reaction rate. You can convert an + overall reaction rate (kon) to an intrinsic reaction rate (ka) with + the function utils.k_a(kon, kD), but only for reaction rules in 3D. + + By default an EGFRDSimulator will assume a repulsive + bimolecular reaction rule (ka=0) for each possible combination of + reactants for which no bimolecular reaction rule is specified. + You can explicitly add these reaction rules to the model with the + method model.ParticleModel.set_all_repulsive. + + """ +\end{verbatim} + +\begin{verbatim} +def create_unbinding_reaction_rule(reactant, product1, product2, kd): + """Example: A -> B + C. + + Arguments: + - reactant + a Species. + - product1 + a Species. + - product2 + a Species. + - kd + intrinsic reaction rate. Units: per second. (Rough order of + magnitude: 1e-2 /s to 1e2 /s). + + The reactant and the products should be in/on the same + Region or Surface. + + An unbinding reaction rule always has exactly two products. + + kd should be an *intrinsic* reaction rate. You can convert an + overall reaction rate (koff) for this reaction rule to an intrinsic + reaction rate (kd) with the function utils.k_d(koff, kon, kD) or + utils.k_d_using_ka(koff, ka, kD). + + An unbinding reaction rule defines a Poissonian process. + + """ +\end{verbatim} + +To put this reactions into effect one should also call \texttt{add_reaction_rule}, e.g.: +\begin{verbatim} + r1 = model.create_binding_reaction_rule(A, B, C, kf) + m.network_rules.add_reaction_rule(r1) +\end{verbatim} + +\subsection{Functions from \texttt{gfrdbase.py}} + +\subsubsection{Core functions} + +\begin{verbatim} +def create_world(m, matrix_size=10): + """Create a world object. + + The world object keeps track of the positions of the particles + and the protective domains during an eGFRD simulation. + + Arguments: + - m + a ParticleModel previously created with model.ParticleModel. + - matrix_size + the number of cells in the MatrixSpace along the x, y and z + axis. Leave it to the default number if you don't know what + to put here. + + The simulation cube "world" is divided into (matrix_size x matrix_size + x matrix_size) cells. Together these cells form a MatrixSpace. The + MatrixSpace keeps track in which cell every particle and protective + domain is at a certain point in time. To find the neigherest + neighbours of particle, only objects in the same cell and the 26 + (3x3x3 - 1) neighbouring cells (the simulation cube has periodic + boundary conditions) have to be taken into account. + + The matrix_size limits the size of the protective domains. If you + have fewer particles, you want a smaller matrix_size, such that the + protective domains and thus the eGFRD timesteps can be larger. If + you have more particles, you want a larger matrix_size, such that + finding the neigherest neighbours is faster. + + Example. In samples/dimer/dimer.py a matrix_size of + (N * 6) ** (1. / 3.) is used, where N is the average number of + particles in the world. + + """ +\end{verbatim} + + +\subsubsection{Handling objects} + +\begin{verbatim} +def get_closest_surface(world, pos, ignore): + """Return + - closest surface + - distance to closest surface + + We can not use matrix_space, it would miss a surface if the + origin of the surface would not be in the same or neighboring + cells as pos.""" +\end{verbatim} + +\begin{verbatim} +def get_closest_surface_within_radius(world, pos, radius, ignore): + """Return: + - surface within radius or None + - closest surface (regardless of radius) + - distance to closest surface""" +\end{verbatim} + +\subsubsection{Adding particles} +\begin{verbatim} +functions +def throw_in_particles(world, sid, n): + """Add n particles of a certain Species to the specified world. + + Arguments: + - sid + a Species previously created with the function + model.Species. + - n + the number of particles to add. + + Make sure to first add the Species to the model with the method + model.ParticleModel.add_species_type. + + """ +\end{verbatim} + +\begin{verbatim} +def place_particle(world, sid, position): + """Place a particle of a certain Species at a specific position in + the specified world. + + Arguments: + - sid + a Species previously created with the function + model.Species. + - position + a position vector [x, y, z]. Units: [meters, meters, meters]. + + Make sure to first add the Species to the model with the method + model.ParticleModel.add_species_type. + + """ +\end{verbatim} + +\subsection{Functions from \texttt{egfrd.py}} + +\subsubsection{Core functions} +\begin{verbatim} +class EGFRDSimulator(ParticleSimulatorBase): + """ + """ + def __init__(self, world, rng=myrandom.rng, network_rules=None): + """Create a new EGFRDSimulator. + + Arguments: + - world + a world object created with the function + gfrdbase.create_world. + - rng + a random number generator. By default myrandom.rng is + used, which uses Mersenne Twister from the GSL library. + You can set the seed of it with the function + myrandom.seed. + - network_rules + you don't need to use this, for backward compatibility only. + + """ +\end{verbatim} + +\begin{verbatim} + def step(self): + """Execute one eGFRD step. + + """ +\end{verbatim} +(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) + +\begin{verbatim} + def stop(self, t): + """Synchronize all particles at time t. + + With eGFRD, particle positions are normally updated + asynchronously. This method bursts all protective domains and + assigns a position to each particle. + + Arguments: + - t + the time at which to synchronize the particles. Usually + you will want to use the current time of the simulator: + EGFRDSimulator.t. + + This method is called stop because it is usually called at the + end of a simulation. It is possible to call this method at an + earlier time. For example the Logger module does this, because + it needs to know the positions of the particles at each log + step. + + """ +\end{verbatim} +(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) + +\subsubsection{Simulator time (manipulation) functions} + +\begin{verbatim} + def get_next_time(self): + """ + Returns the time it will be when the next egfrd timestep + is completed. + """ +\end{verbatim} +(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) + + +\begin{verbatim} + def reset(self): + """ + This function resets the "records" of the simulator. This means + the simulator time is reset, the step counter is reset, events + are reset, etc. + Can be for example usefull when users want to "stirr" the + simulation before starting the "real experiment". + """ +\end{verbatim} +(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) + +\subsubsection{Get data} +\begin{verbatim} + def print_report(self, out=None): + """Print various statistics about the simulation. + + Arguments: + - None + + """ +\end{verbatim} + +\subsubsection{Additional functions} +The class \texttt{EGFRDSimulator} contains several functions which might be usefull to eGFRD users in very specific cases. Because of their specific nature, they don't contain docstrings. +\begin{verbatim} + def get_matrix_cell_size(self): + return self.containers[0].cell_size + + def set_user_max_shell_size(self, size): + self.user_max_shell_size = size + + def get_user_max_shell_size(self): + return self.user_max_shell_size + + def get_max_shell_size(self): + return min(self.get_matrix_cell_size() * .5 / SAFETY, + self.user_max_shell_size) +\end{verbatim} + +\subsection{Functions from \texttt{dumper.py}} + +\subsubsection{Getting information on species/particles} +\begin{verbatim} +def get_species(sim): + """Return an iterator over the Species in the simulator. + + Arguments: + - sim + an EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def dump_species(sim): + """Return a string containing the Species in the simulator. + + Arguments: + - sim + an EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def get_species_names(sim): + """Return an iterator over the names of the Species in the + simulator. + + Arguments: + - sim + an EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def dump_species_names(sim): + """Return a string containing the names of the Species in the + simulator. + + Arguments: + - sim + an EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def _get_species_type_by_name(sim, name): + """ Return the type of a species with a certain name + + Arguments: + - sim + an EGFRDSimulator + - name + species name + """ +\end{verbatim} + +\begin{verbatim} +def _get_particles_by_sid(sim, sid): + """ Return a generator (using "yield") to loop over (pid, particle). + + Arguments: + - sim + an EGFRDSimulator + - sid + ID of a species + + E.g.: + + myparticles = _get_particles_by_sid(sim, sid) + + for mypid, myparticle in myparticles: + print str(str(mypid), str(myparticle)) + """ +\end{verbatim} + +\begin{verbatim} +def get_particles(sim, identifier=None): + """Return an iterator over the + (particle identifier, particle)-pairs in the simulator. + + Arguments: + - sim + an EGFRDSimulator. + - identifier + a Species or the name of a Species. If none is specified, + all (particle identifier, particle)-pairs will be returned. + + """ +\end{verbatim} + +\begin{verbatim} +def dump_particles(sim, identifier=None): + """Return a string containing the + (particle identifier, particle)-pairs in the simulator. + + Arguments: + - sim + an EGFRDSimulator. + - identifier + a Species or the name of a Species. If none is specified, + all (particle identifier, particle)-pairs will be returned. + + """ +\end{verbatim} + +\begin{verbatim} +def _get_number_of_particles_by_sid(sim, sid): + """ + Returns the number of particles of a certain species. + + Arguments: + - sim + an EGFRDSimulator. + - sid + ID of a species + + """ +\end{verbatim} + +\begin{verbatim} +def get_number_of_particles(sim, identifier=None): + """Return the number of particles of a certain Species in the + simulator. + + Arguments: + - sim + either an EGFRDSimulator or a GillespieSimulator. + - identifier + a Species. Optional. If none is specified, a list of + (Species name, number of particles)-pairs will be returned. + + """ +\end{verbatim} + +\begin{verbatim} +def dump_number_of_particles(sim, identifier=None): + """Return a string containing the number of particles of a certain + Species in the simulator. + + Arguments: + - sim + either an EGFRDSimulator or a GillespieSimulator. + - identifier + a Species. Optional. If none is specified, + a string of (Species name, number of particles)-pairs will + be returned. + + """ +\end{verbatim} + +\subsubsection{Get information on domains} + +\begin{verbatim} +def get_domains(egfrdsim): + """Return an iterator over the protective domains in the simulator. + + Arguments: + - egfrdsim + an EGFRDSimulator + """ +\end{verbatim} + +\begin{verbatim} +def dump_domains(egfrdsim): + """Return an string containing the protective domains in the + simulator. + + """ +\end{verbatim} + +subsubsection{Get information on reaction rules} + +\begin{verbatim} +def get_reaction_rules(model_or_simulator): + """Return three lists with all the reaction rules defined in the + ParticleModel or EGFRDSimulator. + + The three lists are: + - reaction rules of only one reactant. + - reaction rules between two reactants with a reaction rate + larger than 0. + - repulsive reaction rules between two reactants with a + reaction rate equal to 0. + + Arguments: + - model_or_simulator + a ParticleModel or EGFRDSimulator. + + """ +\end{verbatim} + +\begin{verbatim} +def _dump_reaction_rule(model, reaction_rule): + """Helper. Return ReactionRule as string. + + ReactionRule.__str__ would be good, but we are actually getting a + ReactionRuleInfo or ReactionRuleCache object.""" +\end{verbatim} + +\begin{verbatim} +def dump_reaction_rules(model_or_simulator): + """Return a formatted string containing all the reaction rules + defined in the ParticleModel or EGFRDSimulator. + + Arguments: + - model_or_simulator + a ParticleModel or EGFRDSimulator. + + """ +\end{verbatim} + +\subsection{Functions from \texttt{utils.py}} + +This file contains functions on common mathematical objects, transformations and formulas. Some of these functions are used throughout the algorithm, and some are supplied for convenience. +For the user, none of these functions are \textit{needed} to make a simulation work, but some might come in handy. + +\subsubsection{Mathematical comparisons} +\begin{verbatim} +def feq(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a and b are equal, subject to given tolerances. + Float comparison. + + Also see numpy.allclose(). + + The (relative) tolerance must be positive and << 1.0 + + Instead of specifying an absolute tolerance, you can speciy a + typical value for a or b. The absolute tolerance is then the + relative tolerance multipied by this typical value, and will be + used when comparing a value to zero. By default, the typical + value is 1.""" + +def fgreater(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a is greater than b, subject to given tolerances. + Float comparison.""" + +def fless(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a is less than b, subject to given tolerances. + Float comparison.""" + +def fgeq(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a is greater or equal than b, subject to given + tolerances. Float comparison.""" + +def fleq(a, b, typical=1, tolerance=TOLERANCE): + """Return True if a is less than or equal than b, subject to given + tolerances. Float comparison.""" + +\end{verbatim} + +\subsubsection{Conversions} + +As these functions only contain a single line of formula code, this line is also supplied. +\begin{verbatim} +def per_M_to_m3(rate): + """Convert a reaction rate from units 'per molar per second' to + units 'meters^3 per second'. + + """ + return rate / (1000 * N_A) + +def per_microM_to_m3(rate): + """Convert a reaction rate from units 'per micromolar per second' to + units 'meters^3 per second'. + + """ + return per_M_to_m3(rate * 1e6) + +def M_to_per_m3(molar): + """Convert a concentration from units 'molar' to units 'per + meters^3'. + + """ + return molar * (1000 * N_A) + +def microM_to_per_m3(micromolar): + """Convert a concentration from units 'micromolar' to units 'per + meters^3'. + + """ + return M_to_per_m3(micromolar / 1e6) + +def C2N(c, V): + """Calculate the number of particles in a volume 'V' (dm^3) + with a concentration 'c' (mol/dm^3). + + """ + return c * V * N_A # round() here? +\end{verbatim} + +Conversions involving rates: +\begin{verbatim} + +def k_D(Dtot, sigma): + """Calculate the 'pseudo-'reaction rate (kD) caused by diffusion. + + kD is equal to 1 divided by the time it takes for two particles to + meet each other by diffusion. It is needed when converting from + an intrinsic reaction rate to an overall reaction rates or vice + versa. + + Example: + - A + B -> C. + + Arguments: + - Dtot: + the diffusion constant of particle A plus the diffusion + constant of particle B. Units: meters^2/second. + - sigma + the radius of particle A plus the radius of particle B. + Units: meters. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + return 4.0 * numpy.pi * Dtot * sigma + +def k_a(kon, kD): + """Convert an overall reaction rate (kon) for a binding/annihilation + reaction rule to an intrinsic reaction rate (ka). + + Example: + - A + B -> C + binding reaction rule + - A + B -> 0 + annihilation reaction rule + + Arguments: + - kon + the overall reaction rate for the reaction rule. Units: + meters^3/second. + - kD + the 'pseudo-'reaction rate caused by the diffusion of + particles A and B. See the function k_D(). Units: + meters^3/second. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + if kon > kD: + raise RuntimeError, 'kon > kD.' + ka = 1. / ((1. / kon) - (1. / kD)) + return ka + +def k_d(koff, kon, kD): + """Convert an overall reaction rate (koff) for an unbinding reaction + rule to an intrinsic reaction rate (kd). + + This one is a bit tricky. We consider reaction rules with only 1 + reactant. In case there is only 1 product also, no conversion in + necessary. But when the reaction rule has 2 products, we need to + take the reverse reaction rule into account and do the proper + conversion. + + Example: + - C -> A + B + unbinding reaction rule + - A + B -> C + reverse reaction rule + + Arguments: + - koff + the overall reaction rate for the unbinding reaction rule. + Units: meters^3/second. + - kon + the overall reaction rate for the reverse reaction rule. + Units: meters^3/second. + - kD + the 'pseudo-'reaction rate caused by the diffusion of + particles A and B. See the function k_D(). Units: + meters^3/second. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + ka = k_a(kon, kD) + kd = k_d_using_ka(koff, ka, kD) + return kd + +def k_d_using_ka(koff, ka, kD): + """Convert an overall reaction rate (koff) for an unbinding reaction + rule to an intrinsic reaction rate (kd). + + Similar to the function k_d(), but expects an intrinsic rate (ka) + instead of an overall rate (kon) for the reversed reaction rule as + the second argument. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + kd = koff * (1 + float(ka) / kD) + return kd + +def k_on(ka, kD): + """Convert an intrinsic reaction rate (ka) for a binding/annihilation + reaction rule to an overall reaction rate (kon). + + The inverse of the function k_a(). + + Rarely needed. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + kon = 1. / ((1. / kD) + (1. / ka)) # m^3/s + return kon + +def k_off(kd, kon, kD): + """Convert an intrinsic reaction rate (kd) for an unbinding reaction + rule to an overall reaction rate (koff). + + The inverse of the function k_d(). + + Rarely needed. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + ka = k_a(kon, kD) + koff = k_off_using_ka(kd, ka, kD) + return koff + +def k_off_using_ka(kd, ka, kD): + """Convert an intrinsic reaction rate (kd) for an unbinding reaction + rule to an overall reaction rate (koff). + + Similar to the function k_off(), but expects an intrinsic rate + (ka) instead of an overall rate (kon) as the second argument. + + Rarely needed. + + This function is only available for reaction rules in 3D. No + analytical expression for kD in 1D or 2D is currently known. + + """ + koff = 1. / (float(ka) / (kd * kD) + (1. / kd)) + return koff +\end{verbatim} + + +\subsubsection{Some convenient functoins} + +These functions do not contain docstrings, are sometimes self-explanatory and probably not needed that often in simulations. Therefore only definitions of functions that might be of interest to the user are listed here: + +Mean arrival time: +\begin{verbatim} +def mean_arrival_time(r, D): + return (r * r) / (6.0 * D) +\end{verbatim} + +Calculating with distances: +\begin{verbatim} +def distance_sq_array_simple(position1, positions, fsize = None): + +def distance_array_simple(position1, positions, fsize = None): + +distance = _gfrd.distance + +distance_cyclic = _gfrd.distance_cyclic + +def distance_sq_array_cyclic(position1, positions, fsize): + +def distance_array_cyclic(position1, positions, fsize = 0): + +\end{verbatim} + +Some vector functions: +\begin{verbatim} +def cartesian_to_spherical(c): + +def spherical_to_cartesian(s): + +def random_unit_vector_s(): + +def random_unit_vector(): + +def random_vector(r): + +def random_vector2D(r): + +def length(a): + +def normalize(a, l=1): + +def vector_angle(a, b): + +def vector_angle_against_z_axis(b): + +def crossproduct(a, b): + +def crossproduct_against_z_axis(a): + +def rotate_vector(v, r, alpha): +\end{verbatim} + +\subsection{Notes on other files} + +\subsubsection{\texttt{bd.py}: Brownian Dynamic Simulator} +The class \texttt{BDSimulator} can be used in the same way as the EGFRDSimulator class, but performs Brownian Dynamics (BD) instead. Users who want to perform eGFRD simulations never need this. The simulator can be used for comparison of the eGFRD algorithm with Brownian Dynamics. + +\subsubsection{\texttt{gillespie.py}: Gillespie Simulator} +Similar to the BD simulator, the class \texttt{GillespieSimulatorBase} can be used for Gillespie type simulations. + +\subsubsection{\texttt{legacy.py}: Old redundant functions} +This module is called nowhere in the Python code. It contains an archive of outdated code. + +\subsubsection{\texttt{multi.py}, \texttt{pair.py}, \texttt{single.py}} +These file contain the code that handle the specific events that happen in the different categories of domains. + +\subsubsection{\texttt{myrandom.py}} +Contains a few convenient lines of code used when using random functions. + +\subsubsection{\texttt{make\_cjy\_table.py.py}, \texttt{make\_sjy\_table.py.py}} +Generate (respectively cylindrical and spherical) bessel function tables. + + +\subsection{Function from module \texttt{logger.py}} + +This module contains two loggers. One logger that logs in the hdf5 format, for obvious reasons in class \texttt{HDF5Logger}. Note that this logger requires the module h5py. The other logger gives output dictated more by the nature of the eGFRD algorithm. +The loggers are not (yet) explained in very much detail here, but both function in the same way. A logger class is made, which takes input on to which file to write, the logger can be started by \texttt{start()} and steps are logged by the function \texttt{log()}. + +\subsubsection{HDF5 logger} + +\begin{verbatim} +class HDF5Logger(object): + def __init__(self, logname, directory='data', split=False): + + def log(self, sim, time): + + def start(self, sim): +\end{verbatim} + +\subsubsection{"Normal" logger} +\begin{verbatim} +class Logger(object): + def __init__(self, logname='log', directory='data', comment=''): + + def log(self, sim, time): + + def start(self, sim): +\end{verbatim} + +(Note that not all functions contained by this class are listed, just functions deemed usefull for user interface.) + +\section{Todo} + +- \texttt{sid = identifier.id} gives the sid, but what exactly is identifier? + +\end{document} diff --git a/doc/interface_functions.toc b/doc/interface_functions.toc new file mode 100644 index 00000000..9131d2db --- /dev/null +++ b/doc/interface_functions.toc @@ -0,0 +1,33 @@ +\contentsline {section}{\numberline {1}Introduction}{1} +\contentsline {section}{\numberline {2}Functions}{1} +\contentsline {subsection}{\numberline {2.1}Functions from \texttt {model.py}}{1} +\contentsline {subsubsection}{\numberline {2.1.1}Core functions:}{1} +\contentsline {subsubsection}{\numberline {2.1.2}Creating regions}{2} +\contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{4} +\contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{7} +\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{7} +\contentsline {subsubsection}{\numberline {2.2.2}Handling objects}{8} +\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{8} +\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{9} +\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{9} +\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{10} +\contentsline {subsubsection}{\numberline {2.3.3}Get data}{10} +\contentsline {subsubsection}{\numberline {2.3.4}Additional functions}{10} +\contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{11} +\contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{11} +\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{13} +\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{14} +\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{15} +\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{15} +\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{19} +\contentsline {subsection}{\numberline {2.6}Notes on other files}{20} +\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{20} +\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{20} +\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{20} +\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{20} +\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{21} +\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{21} +\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{21} +\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{21} +\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{21} +\contentsline {section}{\numberline {3}Todo}{21} diff --git a/samples/template/README b/samples/template/README index 3a85ba66..9ba19558 100644 --- a/samples/template/README +++ b/samples/template/README @@ -4,3 +4,32 @@ General lay-out of a simulation ===================================== + + # ### Import correct modules +import sys +from egfrd import * +from bd import * +import model +import gfrdbase +import myrandom + + # ### Define model + + m = model.ParticleModel(1e-3) + + A = model.Species('A', 0.0, sigma/2) + m.add_species_type(A) + + r1 = model.create_binding_reaction_rule(A, B, C, kf) + m.network_rules.add_reaction_rule(r1) + + # ### Set up simulator + + + w = gfrdbase.create_world(m, 3) + nrw = gfrdbase.create_network_rules_wrapper(m) + s = EGFRDSimulator(w, myrandom.rng, nrw) + + # ### Place particles + + # ### Run simulation diff --git a/utils.py b/utils.py index 685cbbd6..4f746960 100644 --- a/utils.py +++ b/utils.py @@ -28,39 +28,39 @@ # Float comparison functions. def feq(a, b, typical=1, tolerance=TOLERANCE): - # Return True if a and b are equal, subject to given tolerances. - # Float comparison. + """Return True if a and b are equal, subject to given tolerances. + Float comparison. - # Also see numpy.allclose(). + Also see numpy.allclose(). - # The (relative) tolerance must be positive and << 1.0 + The (relative) tolerance must be positive and << 1.0 - # Instead of specifying an absolute tolerance, you can speciy a - # typical value for a or b. The absolute tolerance is then the - # relative tolerance multipied by this typical value, and will be - # used when comparing a value to zero. By default, the typical - # value is 1. + Instead of specifying an absolute tolerance, you can speciy a + typical value for a or b. The absolute tolerance is then the + relative tolerance multipied by this typical value, and will be + used when comparing a value to zero. By default, the typical + value is 1.""" return abs(a - b) < tolerance * (typical + min(abs(a), abs(b))) def fgreater(a, b, typical=1, tolerance=TOLERANCE): - # Return True if a is greater than b, subject to given tolerances. - # Float comparison. + """Return True if a is greater than b, subject to given tolerances. + Float comparison.""" return a - b > tolerance * (typical + min(abs(a), abs(b))) def fless(a, b, typical=1, tolerance=TOLERANCE): - # Return True if a is less than b, subject to given tolerances. - # Float comparison. + """Return True if a is less than b, subject to given tolerances. + Float comparison.""" return b - a > tolerance * (typical + min(abs(a), abs(b))) def fgeq(a, b, typical=1, tolerance=TOLERANCE): - # Return True if a is greater or equal than b, subject to given - # tolerances. Float comparison. + """Return True if a is greater or equal than b, subject to given + tolerances. Float comparison.""" diff = a - b barrier = tolerance * (typical + min(abs(a), abs(b))) @@ -69,8 +69,8 @@ def fgeq(a, b, typical=1, tolerance=TOLERANCE): def fleq(a, b, typical=1, tolerance=TOLERANCE): - # Return True if a is less than or equal than b, subject to given - # tolerances. Float comparison. + """Return True if a is less than or equal than b, subject to given + tolerances. Float comparison.""" diff = b - a barrier = tolerance * (typical + min(abs(a), abs(b))) @@ -195,9 +195,9 @@ def crossproduct_against_z_axis(a): return numpy.array([- a[1], a[0], 0.0]) def rotate_vector(v, r, alpha): - # v: vector to rotate - # r: normalized rotation axis - # alpha: rotation angle in radian + """v: vector to rotate + r: normalized rotation axis + alpha: rotation angle in radian""" cosalpha = math.cos(alpha) sinalpha = math.sin(alpha) From 0969456205d77aef8ce046643a0e43cec00cbe64 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Fri, 13 May 2011 12:10:53 +0200 Subject: [PATCH 0029/1541] Deleted .pdf from doc. --- doc/interface_functions.pdf | Bin 92315 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/interface_functions.pdf diff --git a/doc/interface_functions.pdf b/doc/interface_functions.pdf deleted file mode 100644 index 23375729af9377b1c371b3e36a732439af741ba7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92315 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 HNn-s!kSPqG From 9891ccf9ebecf969028f0f09676a67e8941782e2 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Fri, 13 May 2011 12:12:35 +0200 Subject: [PATCH 0030/1541] Now the files to generate interface_functions.pdf have been added correctly to the commit. Makefile.am is edited, now ./configure will automatically generate the makefile which also makes the interface_functions.pdf. File interface_functions contains all functions I deem usefull for users. Revisions to this file are very welcome. -MW --- doc/interface_functions.tex~ | 1107 ---------------------------------- 1 file changed, 1107 deletions(-) delete mode 100644 doc/interface_functions.tex~ diff --git a/doc/interface_functions.tex~ b/doc/interface_functions.tex~ deleted file mode 100644 index 9181447f..00000000 --- a/doc/interface_functions.tex~ +++ /dev/null @@ -1,1107 +0,0 @@ -\documentclass[a4paper,10pt]{article} -\usepackage[utf8x]{inputenc} - -%opening -\title{Interface functions} -\author{Martijn Wehrens} - -\begin{document} - -\maketitle - -\tableofcontents - -\section{Introduction} -This document gives an overview of functions that are usefull to end-users of the enhanced Green's Functions Reaction Dynamics (eGFRD) algorithm. In other words, it lists the "interface functions". -Basically, this entails a list of function definition plus their so-called python "docstrings", obtained straight from the code. -Note that functions starting with an underscore are actually C++ functions ported to python by boost. - -\section{Functions} - -\subsection{Functions from \texttt{model.py}} - -\subsubsection{Core functions:} -As mentioned, all documentation in this document comes straight from the code. To obtain more information on how to structure a simple script, one could take a look into the sample directory. - - -The class you need to set up a particle model: -\begin{verbatim} - class ParticleModel(_gfrd.Model): - """ - """ - def __init__(self, world_size): - """Create a new ParticleModel. - - Arguments: - - world_size - the size of one side of the simulation "world". Units: - meters. - - The simulation "world" is always assumed to be a cube with - *periodic boundary conditions*, with 1 corner at [0, 0, 0] and - the corner furthest away from [0, 0, 0] being at - [world_size, world_size, world_size]. - - """ -\end{verbatim} - -\begin{verbatim} -def Species(name, D, radius=0, structure="world", drift=0): - """Define a new Species (in/on a specific Region or Surface). - - Arguments: - - name - the name of this Species. - - D - the diffusion constant for this Species in/on this - Region or Surface. Units: meters^2/second. - - radius - the radius for this Species in/on this Region or Surface. - Units: meters. - - structure - the Region or Surface in/on which this Species can exist. - Optional. If you do not specify a Structure the Species is - added to the "world". - - drift - the drift term for this ParticleType on a - CylindricalSurface (1D drift). Units: meters/second. - Optional. - - If a certain Species should be able to exist in the "world" as - well as in/on one of the previously created Regions or Surfaces, - then two distinct Species should be created. One with and one - without an explicit Structure argument. - - """ -\end{verbatim} - -Species should be added to the model: -\begin{verbatim} - - def add_reaction_rule(self, reaction_rule): - """Add a ReactionRule to the ParticleModel. - - Argument: - - reaction rule - a ReactionRule created by one of the functions - model.create_<>_reaction_rule. - - """ -\end{verbatim} - -\subsubsection{Creating regions} - -\begin{verbatim} -_gfrd.create_cuboidal_region.__doc__ = \ -"""create_cuboidal_region(id, corner, diagonal) - -Create and return a new cuboidal Region. - -Arguments: - - id - a descriptive name. - - corner - the point [x, y, z] of the cuboidal Region closest to - [0, 0, 0]. Units: [meters, meters, meters] - - diagonal - the vector [x, y, z] from the corner closest to [0, 0, 0], to - the corner furthest away from [0, 0, 0]. Units: - [meters, meters, meters] - -""" - -_gfrd.create_cylindrical_surface.__doc__ = \ -"""create_cylindrical_surface(id, corner, radius, orientation, length) - -Create and return a new cylindrical Surface. - -Arguments: - - id - a descriptive name. - - corner - the point [x, y, z] on the axis of the cylinder closest to - [0, 0, 0]. Units: [meters, meters, meters] - - radius - the radius of the cylinder. Units: meters. - - orientation - the unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] along the - axis of the cylinder. - - length - the length of the cylinder. Should be equal to the world_size. - Units: meters. - -Surfaces are not allowed to touch or overlap. - -""" - -_gfrd.create_planar_surface.__doc__ = \ -"""create_planar_surface(id, corner, unit_x, unit_y, length_x, length_y) - -Create and return a new planar Surface. - -Arguments: - - id - a descriptive name. - - corner - the point [x, y, z] on the plane closest to [0, 0, 0]. Units: - [meters, meters, meters] - - unit_x - a unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] along the - plane. - - unit_y - a unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] along the plane - and perpendicular to unit_x. - - length_x - the length of the plane along the unit vector unit_x. Should be - equal to the world_size. Units: meters. - - length_y - the length of the plane along the unit vector unit_y. Should be - equal to the world_size. Units: meters. - -Surfaces are not allowed to touch or overlap. - -""" -\end{verbatim} - -As particles, regions should be added to the model. -\begin{verbatim} - def add_structure(self, structure): - """Add a Structure (Region or Surface) to the ParticleModel. - - Arguments: - - structure - a Region or Surface created with one of the functions - model.create_<>_region or model.create_<>_surface. - - """ - assert isinstance(structure, _gfrd.Structure) - self.structures[structure.id] = structure - return structure -\end{verbatim} - -\subsubsection{Adding reaction rules to the model} - -Function set\_all\_repulsive is called automatically, but it's docstring is instructive. -\begin{verbatim} - def set_all_repulsive(self): - """Set all 'other' possible ReactionRules to be repulsive. - - By default an EGFRDSimulator will assume: - - a repulsive bimolecular reaction rule (k=0) for each - possible combination of reactants for which no - bimolecular reaction rule is specified. - - This method explicitly adds these ReactionRules to the - ParticleModel. - - """ -\end{verbatim} - -\begin{verbatim} -def create_unimolecular_reaction_rule(reactant, product, k): - """Example: A -> B. - - Arguments: - - reactant - a Species. - - product - a Species. - - k - reaction rate. Units: per second. (Rough order of magnitude: - 1e-2 /s to 1e2 /s). - - The reactant and the product should be in/on the same - Region or Surface. - - There is no distinction between an intrinsic and an overall reaction - rate for a unimolecular ReactionRule. - - A unimolecular reaction rule defines a Poissonian process. - - """ -\end{verbatim} - -\begin{verbatim} -def create_decay_reaction_rule(reactant, k): - """Example: A -> 0. - - Arguments: - - reactant - a Species. - - k - reaction rate. Units: per second. (Rough order of magnitude: - 1e-2 /s to 1e2 /s). - - There is no distinction between an intrinsic and an overall reaction - rate for a decay ReactionRule. - - A decay reaction rule defines a Poissonian process. - - """ -\end{verbatim} - -\begin{verbatim} -def create_annihilation_reaction_rule(reactant1, reactant2, ka): - """Example: A + B -> 0. - - Arguments: - - reactant1 - a Species. - - reactant2 - a Species. - - ka - intrinsic reaction rate. Units: meters^3 per second. (Rough - order of magnitude: 1e-16 m^3/s to 1e-20 m^3/s). - - The reactants should be in/on the same Region or Surface. - - ka should be an *intrinsic* reaction rate. You can convert an - overall reaction rate (kon) to an intrinsic reaction rate (ka) with - the function utils.k_a(kon, kD), but only for reaction rules in 3D. - - By default an EGFRDSimulator will assume a repulsive - bimolecular reaction rule (ka=0) for each possible combination of - reactants for which no bimolecular reaction rule is specified. - You can explicitly add these reaction rules to the model with the - method model.ParticleModel.set_all_repulsive. - - """ -\end{verbatim} - -\begin{verbatim} -def create_binding_reaction_rule(reactant1, reactant2, product, ka): - """Example: A + B -> C. - - Arguments: - - reactant1 - a Species. - - reactant2 - a Species. - - product - a Species. - - ka - intrinsic reaction rate. Units: meters^3 per second. (Rough - order of magnitude: 1e-16 m^3/s to 1e-20 m^3/s) - - The reactants and the product should be in/on the same - Region or Surface. - - A binding reaction rule always has exactly one product. - - ka should be an *intrinsic* reaction rate. You can convert an - overall reaction rate (kon) to an intrinsic reaction rate (ka) with - the function utils.k_a(kon, kD), but only for reaction rules in 3D. - - By default an EGFRDSimulator will assume a repulsive - bimolecular reaction rule (ka=0) for each possible combination of - reactants for which no bimolecular reaction rule is specified. - You can explicitly add these reaction rules to the model with the - method model.ParticleModel.set_all_repulsive. - - """ -\end{verbatim} - -\begin{verbatim} -def create_unbinding_reaction_rule(reactant, product1, product2, kd): - """Example: A -> B + C. - - Arguments: - - reactant - a Species. - - product1 - a Species. - - product2 - a Species. - - kd - intrinsic reaction rate. Units: per second. (Rough order of - magnitude: 1e-2 /s to 1e2 /s). - - The reactant and the products should be in/on the same - Region or Surface. - - An unbinding reaction rule always has exactly two products. - - kd should be an *intrinsic* reaction rate. You can convert an - overall reaction rate (koff) for this reaction rule to an intrinsic - reaction rate (kd) with the function utils.k_d(koff, kon, kD) or - utils.k_d_using_ka(koff, ka, kD). - - An unbinding reaction rule defines a Poissonian process. - - """ -\end{verbatim} - -To put this reactions into effect one should also call \texttt{add_reaction_rule}, e.g.: -\begin{verbatim} - r1 = model.create_binding_reaction_rule(A, B, C, kf) - m.network_rules.add_reaction_rule(r1) -\end{verbatim} - -\subsection{Functions from \texttt{gfrdbase.py}} - -\subsubsection{Core functions} - -\begin{verbatim} -def create_world(m, matrix_size=10): - """Create a world object. - - The world object keeps track of the positions of the particles - and the protective domains during an eGFRD simulation. - - Arguments: - - m - a ParticleModel previously created with model.ParticleModel. - - matrix_size - the number of cells in the MatrixSpace along the x, y and z - axis. Leave it to the default number if you don't know what - to put here. - - The simulation cube "world" is divided into (matrix_size x matrix_size - x matrix_size) cells. Together these cells form a MatrixSpace. The - MatrixSpace keeps track in which cell every particle and protective - domain is at a certain point in time. To find the neigherest - neighbours of particle, only objects in the same cell and the 26 - (3x3x3 - 1) neighbouring cells (the simulation cube has periodic - boundary conditions) have to be taken into account. - - The matrix_size limits the size of the protective domains. If you - have fewer particles, you want a smaller matrix_size, such that the - protective domains and thus the eGFRD timesteps can be larger. If - you have more particles, you want a larger matrix_size, such that - finding the neigherest neighbours is faster. - - Example. In samples/dimer/dimer.py a matrix_size of - (N * 6) ** (1. / 3.) is used, where N is the average number of - particles in the world. - - """ -\end{verbatim} - - -\subsubsection{Handling objects} - -\begin{verbatim} -def get_closest_surface(world, pos, ignore): - """Return - - closest surface - - distance to closest surface - - We can not use matrix_space, it would miss a surface if the - origin of the surface would not be in the same or neighboring - cells as pos.""" -\end{verbatim} - -\begin{verbatim} -def get_closest_surface_within_radius(world, pos, radius, ignore): - """Return: - - surface within radius or None - - closest surface (regardless of radius) - - distance to closest surface""" -\end{verbatim} - -\subsubsection{Adding particles} -\begin{verbatim} -functions -def throw_in_particles(world, sid, n): - """Add n particles of a certain Species to the specified world. - - Arguments: - - sid - a Species previously created with the function - model.Species. - - n - the number of particles to add. - - Make sure to first add the Species to the model with the method - model.ParticleModel.add_species_type. - - """ -\end{verbatim} - -\begin{verbatim} -def place_particle(world, sid, position): - """Place a particle of a certain Species at a specific position in - the specified world. - - Arguments: - - sid - a Species previously created with the function - model.Species. - - position - a position vector [x, y, z]. Units: [meters, meters, meters]. - - Make sure to first add the Species to the model with the method - model.ParticleModel.add_species_type. - - """ -\end{verbatim} - -\subsection{Functions from \texttt{egfrd.py}} - -\subsubsection{Core functions} -\begin{verbatim} -class EGFRDSimulator(ParticleSimulatorBase): - """ - """ - def __init__(self, world, rng=myrandom.rng, network_rules=None): - """Create a new EGFRDSimulator. - - Arguments: - - world - a world object created with the function - gfrdbase.create_world. - - rng - a random number generator. By default myrandom.rng is - used, which uses Mersenne Twister from the GSL library. - You can set the seed of it with the function - myrandom.seed. - - network_rules - you don't need to use this, for backward compatibility only. - - """ -\end{verbatim} - -\begin{verbatim} - def step(self): - """Execute one eGFRD step. - - """ -\end{verbatim} -(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) - -\begin{verbatim} - def stop(self, t): - """Synchronize all particles at time t. - - With eGFRD, particle positions are normally updated - asynchronously. This method bursts all protective domains and - assigns a position to each particle. - - Arguments: - - t - the time at which to synchronize the particles. Usually - you will want to use the current time of the simulator: - EGFRDSimulator.t. - - This method is called stop because it is usually called at the - end of a simulation. It is possible to call this method at an - earlier time. For example the Logger module does this, because - it needs to know the positions of the particles at each log - step. - - """ -\end{verbatim} -(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) - -\subsubsection{Simulator time (manipulation) functions} - -\begin{verbatim} - def get_next_time(self): - """ - Returns the time it will be when the next egfrd timestep - is completed. - """ -\end{verbatim} -(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) - - -\begin{verbatim} - def reset(self): - """ - This function resets the "records" of the simulator. This means - the simulator time is reset, the step counter is reset, events - are reset, etc. - Can be for example usefull when users want to "stirr" the - simulation before starting the "real experiment". - """ -\end{verbatim} -(Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) - -\subsubsection{Get data} -\begin{verbatim} - def print_report(self, out=None): - """Print various statistics about the simulation. - - Arguments: - - None - - """ -\end{verbatim} - -\subsubsection{Additional functions} -The class \texttt{EGFRDSimulator} contains several functions which might be usefull to eGFRD users in very specific cases. Because of their specific nature, they don't contain docstrings. -\begin{verbatim} - def get_matrix_cell_size(self): - return self.containers[0].cell_size - - def set_user_max_shell_size(self, size): - self.user_max_shell_size = size - - def get_user_max_shell_size(self): - return self.user_max_shell_size - - def get_max_shell_size(self): - return min(self.get_matrix_cell_size() * .5 / SAFETY, - self.user_max_shell_size) -\end{verbatim} - -\subsection{Functions from \texttt{dumper.py}} - -\subsubsection{Getting information on species/particles} -\begin{verbatim} -def get_species(sim): - """Return an iterator over the Species in the simulator. - - Arguments: - - sim - an EGFRDSimulator. - - """ -\end{verbatim} - -\begin{verbatim} -def dump_species(sim): - """Return a string containing the Species in the simulator. - - Arguments: - - sim - an EGFRDSimulator. - - """ -\end{verbatim} - -\begin{verbatim} -def get_species_names(sim): - """Return an iterator over the names of the Species in the - simulator. - - Arguments: - - sim - an EGFRDSimulator. - - """ -\end{verbatim} - -\begin{verbatim} -def dump_species_names(sim): - """Return a string containing the names of the Species in the - simulator. - - Arguments: - - sim - an EGFRDSimulator. - - """ -\end{verbatim} - -\begin{verbatim} -def _get_species_type_by_name(sim, name): - """ Return the type of a species with a certain name - - Arguments: - - sim - an EGFRDSimulator - - name - species name - """ -\end{verbatim} - -\begin{verbatim} -def _get_particles_by_sid(sim, sid): - """ Return a generator (using "yield") to loop over (pid, particle). - - Arguments: - - sim - an EGFRDSimulator - - sid - ID of a species - - E.g.: - - myparticles = _get_particles_by_sid(sim, sid) - - for mypid, myparticle in myparticles: - print str(str(mypid), str(myparticle)) - """ -\end{verbatim} - -\begin{verbatim} -def get_particles(sim, identifier=None): - """Return an iterator over the - (particle identifier, particle)-pairs in the simulator. - - Arguments: - - sim - an EGFRDSimulator. - - identifier - a Species or the name of a Species. If none is specified, - all (particle identifier, particle)-pairs will be returned. - - """ -\end{verbatim} - -\begin{verbatim} -def dump_particles(sim, identifier=None): - """Return a string containing the - (particle identifier, particle)-pairs in the simulator. - - Arguments: - - sim - an EGFRDSimulator. - - identifier - a Species or the name of a Species. If none is specified, - all (particle identifier, particle)-pairs will be returned. - - """ -\end{verbatim} - -\begin{verbatim} -def _get_number_of_particles_by_sid(sim, sid): - """ - Returns the number of particles of a certain species. - - Arguments: - - sim - an EGFRDSimulator. - - sid - ID of a species - - """ -\end{verbatim} - -\begin{verbatim} -def get_number_of_particles(sim, identifier=None): - """Return the number of particles of a certain Species in the - simulator. - - Arguments: - - sim - either an EGFRDSimulator or a GillespieSimulator. - - identifier - a Species. Optional. If none is specified, a list of - (Species name, number of particles)-pairs will be returned. - - """ -\end{verbatim} - -\begin{verbatim} -def dump_number_of_particles(sim, identifier=None): - """Return a string containing the number of particles of a certain - Species in the simulator. - - Arguments: - - sim - either an EGFRDSimulator or a GillespieSimulator. - - identifier - a Species. Optional. If none is specified, - a string of (Species name, number of particles)-pairs will - be returned. - - """ -\end{verbatim} - -\subsubsection{Get information on domains} - -\begin{verbatim} -def get_domains(egfrdsim): - """Return an iterator over the protective domains in the simulator. - - Arguments: - - egfrdsim - an EGFRDSimulator - """ -\end{verbatim} - -\begin{verbatim} -def dump_domains(egfrdsim): - """Return an string containing the protective domains in the - simulator. - - """ -\end{verbatim} - -subsubsection{Get information on reaction rules} - -\begin{verbatim} -def get_reaction_rules(model_or_simulator): - """Return three lists with all the reaction rules defined in the - ParticleModel or EGFRDSimulator. - - The three lists are: - - reaction rules of only one reactant. - - reaction rules between two reactants with a reaction rate - larger than 0. - - repulsive reaction rules between two reactants with a - reaction rate equal to 0. - - Arguments: - - model_or_simulator - a ParticleModel or EGFRDSimulator. - - """ -\end{verbatim} - -\begin{verbatim} -def _dump_reaction_rule(model, reaction_rule): - """Helper. Return ReactionRule as string. - - ReactionRule.__str__ would be good, but we are actually getting a - ReactionRuleInfo or ReactionRuleCache object.""" -\end{verbatim} - -\begin{verbatim} -def dump_reaction_rules(model_or_simulator): - """Return a formatted string containing all the reaction rules - defined in the ParticleModel or EGFRDSimulator. - - Arguments: - - model_or_simulator - a ParticleModel or EGFRDSimulator. - - """ -\end{verbatim} - -\subsection{Functions from \texttt{utils.py}} - -This file contains functions on common mathematical objects, transformations and formulas. Some of these functions are used throughout the algorithm, and some are supplied for convenience. -For the user, none of these functions are \textit{needed} to make a simulation work, but some might come in handy. - -\subsubsection{Mathematical comparisons} -\begin{verbatim} -def feq(a, b, typical=1, tolerance=TOLERANCE): - """Return True if a and b are equal, subject to given tolerances. - Float comparison. - - Also see numpy.allclose(). - - The (relative) tolerance must be positive and << 1.0 - - Instead of specifying an absolute tolerance, you can speciy a - typical value for a or b. The absolute tolerance is then the - relative tolerance multipied by this typical value, and will be - used when comparing a value to zero. By default, the typical - value is 1.""" - -def fgreater(a, b, typical=1, tolerance=TOLERANCE): - """Return True if a is greater than b, subject to given tolerances. - Float comparison.""" - -def fless(a, b, typical=1, tolerance=TOLERANCE): - """Return True if a is less than b, subject to given tolerances. - Float comparison.""" - -def fgeq(a, b, typical=1, tolerance=TOLERANCE): - """Return True if a is greater or equal than b, subject to given - tolerances. Float comparison.""" - -def fleq(a, b, typical=1, tolerance=TOLERANCE): - """Return True if a is less than or equal than b, subject to given - tolerances. Float comparison.""" - -\end{verbatim} - -\subsubsection{Conversions} - -As these functions only contain a single line of formula code, this line is also supplied. -\begin{verbatim} -def per_M_to_m3(rate): - """Convert a reaction rate from units 'per molar per second' to - units 'meters^3 per second'. - - """ - return rate / (1000 * N_A) - -def per_microM_to_m3(rate): - """Convert a reaction rate from units 'per micromolar per second' to - units 'meters^3 per second'. - - """ - return per_M_to_m3(rate * 1e6) - -def M_to_per_m3(molar): - """Convert a concentration from units 'molar' to units 'per - meters^3'. - - """ - return molar * (1000 * N_A) - -def microM_to_per_m3(micromolar): - """Convert a concentration from units 'micromolar' to units 'per - meters^3'. - - """ - return M_to_per_m3(micromolar / 1e6) - -def C2N(c, V): - """Calculate the number of particles in a volume 'V' (dm^3) - with a concentration 'c' (mol/dm^3). - - """ - return c * V * N_A # round() here? -\end{verbatim} - -Conversions involving rates: -\begin{verbatim} - -def k_D(Dtot, sigma): - """Calculate the 'pseudo-'reaction rate (kD) caused by diffusion. - - kD is equal to 1 divided by the time it takes for two particles to - meet each other by diffusion. It is needed when converting from - an intrinsic reaction rate to an overall reaction rates or vice - versa. - - Example: - - A + B -> C. - - Arguments: - - Dtot: - the diffusion constant of particle A plus the diffusion - constant of particle B. Units: meters^2/second. - - sigma - the radius of particle A plus the radius of particle B. - Units: meters. - - This function is only available for reaction rules in 3D. No - analytical expression for kD in 1D or 2D is currently known. - - """ - return 4.0 * numpy.pi * Dtot * sigma - -def k_a(kon, kD): - """Convert an overall reaction rate (kon) for a binding/annihilation - reaction rule to an intrinsic reaction rate (ka). - - Example: - - A + B -> C - binding reaction rule - - A + B -> 0 - annihilation reaction rule - - Arguments: - - kon - the overall reaction rate for the reaction rule. Units: - meters^3/second. - - kD - the 'pseudo-'reaction rate caused by the diffusion of - particles A and B. See the function k_D(). Units: - meters^3/second. - - This function is only available for reaction rules in 3D. No - analytical expression for kD in 1D or 2D is currently known. - - """ - if kon > kD: - raise RuntimeError, 'kon > kD.' - ka = 1. / ((1. / kon) - (1. / kD)) - return ka - -def k_d(koff, kon, kD): - """Convert an overall reaction rate (koff) for an unbinding reaction - rule to an intrinsic reaction rate (kd). - - This one is a bit tricky. We consider reaction rules with only 1 - reactant. In case there is only 1 product also, no conversion in - necessary. But when the reaction rule has 2 products, we need to - take the reverse reaction rule into account and do the proper - conversion. - - Example: - - C -> A + B - unbinding reaction rule - - A + B -> C - reverse reaction rule - - Arguments: - - koff - the overall reaction rate for the unbinding reaction rule. - Units: meters^3/second. - - kon - the overall reaction rate for the reverse reaction rule. - Units: meters^3/second. - - kD - the 'pseudo-'reaction rate caused by the diffusion of - particles A and B. See the function k_D(). Units: - meters^3/second. - - This function is only available for reaction rules in 3D. No - analytical expression for kD in 1D or 2D is currently known. - - """ - ka = k_a(kon, kD) - kd = k_d_using_ka(koff, ka, kD) - return kd - -def k_d_using_ka(koff, ka, kD): - """Convert an overall reaction rate (koff) for an unbinding reaction - rule to an intrinsic reaction rate (kd). - - Similar to the function k_d(), but expects an intrinsic rate (ka) - instead of an overall rate (kon) for the reversed reaction rule as - the second argument. - - This function is only available for reaction rules in 3D. No - analytical expression for kD in 1D or 2D is currently known. - - """ - kd = koff * (1 + float(ka) / kD) - return kd - -def k_on(ka, kD): - """Convert an intrinsic reaction rate (ka) for a binding/annihilation - reaction rule to an overall reaction rate (kon). - - The inverse of the function k_a(). - - Rarely needed. - - This function is only available for reaction rules in 3D. No - analytical expression for kD in 1D or 2D is currently known. - - """ - kon = 1. / ((1. / kD) + (1. / ka)) # m^3/s - return kon - -def k_off(kd, kon, kD): - """Convert an intrinsic reaction rate (kd) for an unbinding reaction - rule to an overall reaction rate (koff). - - The inverse of the function k_d(). - - Rarely needed. - - This function is only available for reaction rules in 3D. No - analytical expression for kD in 1D or 2D is currently known. - - """ - ka = k_a(kon, kD) - koff = k_off_using_ka(kd, ka, kD) - return koff - -def k_off_using_ka(kd, ka, kD): - """Convert an intrinsic reaction rate (kd) for an unbinding reaction - rule to an overall reaction rate (koff). - - Similar to the function k_off(), but expects an intrinsic rate - (ka) instead of an overall rate (kon) as the second argument. - - Rarely needed. - - This function is only available for reaction rules in 3D. No - analytical expression for kD in 1D or 2D is currently known. - - """ - koff = 1. / (float(ka) / (kd * kD) + (1. / kd)) - return koff -\end{verbatim} - - -\subsubsection{Some convenient functoins} - -These functions do not contain docstrings, are sometimes self-explanatory and probably not needed that often in simulations. Therefore only definitions of functions that might be of interest to the user are listed here: - -Mean arrival time: -\begin{verbatim} -def mean_arrival_time(r, D): - return (r * r) / (6.0 * D) -\end{verbatim} - -Calculating with distances: -\begin{verbatim} -def distance_sq_array_simple(position1, positions, fsize = None): - -def distance_array_simple(position1, positions, fsize = None): - -distance = _gfrd.distance - -distance_cyclic = _gfrd.distance_cyclic - -def distance_sq_array_cyclic(position1, positions, fsize): - -def distance_array_cyclic(position1, positions, fsize = 0): - -\end{verbatim} - -Some vector functions: -\begin{verbatim} -def cartesian_to_spherical(c): - -def spherical_to_cartesian(s): - -def random_unit_vector_s(): - -def random_unit_vector(): - -def random_vector(r): - -def random_vector2D(r): - -def length(a): - -def normalize(a, l=1): - -def vector_angle(a, b): - -def vector_angle_against_z_axis(b): - -def crossproduct(a, b): - -def crossproduct_against_z_axis(a): - -def rotate_vector(v, r, alpha): -\end{verbatim} - -\subsection{Notes on other files} - -\subsubsection{\texttt{bd.py}: Brownian Dynamic Simulator} -The class \texttt{BDSimulator} can be used in the same way as the EGFRDSimulator class, but performs Brownian Dynamics (BD) instead. Users who want to perform eGFRD simulations never need this. The simulator can be used for comparison of the eGFRD algorithm with Brownian Dynamics. - -\subsubsection{\texttt{gillespie.py}: Gillespie Simulator} -Similar to the BD simulator, the class \texttt{GillespieSimulatorBase} can be used for Gillespie type simulations. - -\subsubsection{\texttt{legacy.py}: Old redundant functions} -This module is called nowhere in the Python code. It contains an archive of outdated code. - -\subsubsection{\texttt{multi.py}, \texttt{pair.py}, \texttt{single.py}} -These file contain the code that handle the specific events that happen in the different categories of domains. - -\subsubsection{\texttt{myrandom.py}} -Contains a few convenient lines of code used when using random functions. - -\subsubsection{\texttt{make\_cjy\_table.py.py}, \texttt{make\_sjy\_table.py.py}} -Generate (respectively cylindrical and spherical) bessel function tables. - - -\subsection{Function from module \texttt{logger.py}} - -This module contains two loggers. One logger that logs in the hdf5 format, for obvious reasons in class \texttt{HDF5Logger}. Note that this logger requires the module h5py. The other logger gives output dictated more by the nature of the eGFRD algorithm. -The loggers are not (yet) explained in very much detail here, but both function in the same way. A logger class is made, which takes input on to which file to write, the logger can be started by \texttt{start()} and steps are logged by the function \texttt{log()}. - -\subsubsection{HDF5 logger} - -\begin{verbatim} -class HDF5Logger(object): - def __init__(self, logname, directory='data', split=False): - - def log(self, sim, time): - - def start(self, sim): -\end{verbatim} - -\subsubsection{"Normal" logger} -\begin{verbatim} -class Logger(object): - def __init__(self, logname='log', directory='data', comment=''): - - def log(self, sim, time): - - def start(self, sim): -\end{verbatim} - -(Note that not all functions contained by this class are listed, just functions deemed usefull for user interface.) - -\section{Todo} - -- \texttt{sid = identifier.id} gives the sid, but what exactly is identifier? - -\end{document} From 6f5ae655c4c8d87bf94dfcefda9209cc2cce3d32 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Fri, 13 May 2011 13:49:14 +0200 Subject: [PATCH 0031/1541] Oops! Removed bug from egfrd.py I introduced when adding docstrings without indentation. --- egfrd.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/egfrd.py b/egfrd.py index ec6ce565..f2df374b 100644 --- a/egfrd.py +++ b/egfrd.py @@ -126,10 +126,10 @@ def get_matrix_cell_size(self): return self.containers[0].cell_size def get_next_time(self): - """ - Returns the time it will be when the next egfrd timestep - is completed. - """ #TODO: Added by wehrens@AMOLF.nl; Please revise. + """ + Returns the time it will be when the next egfrd timestep + is completed. + """ #TODO: Added by wehrens@AMOLF.nl; Please revise. if self.scheduler.size == 0: return self.t @@ -146,13 +146,13 @@ def get_max_shell_size(self): self.user_max_shell_size) def reset(self): - """ - This function resets the "records" of the simulator. This means - the simulator time is reset, the step counter is reset, events - are reset, etc. - Can be for example usefull when users want to "stirr" the - simulation before starting the "real experiment". - """ #TODO: Added by wehrens@AMOLF.nl; Please revise. + """ + This function resets the "records" of the simulator. This means + the simulator time is reset, the step counter is reset, events + are reset, etc. + Can be for example usefull when users want to "stirr" the + simulation before starting the "real experiment". + """ #TODO: Added by wehrens@AMOLF.nl; Please revise. self.t = 0.0 self.dt = 0.0 self.step_counter = 0 From dcd7b0ff3cc7983ee1feaab5f201c55302c964bf Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Fri, 13 May 2011 14:04:42 +0200 Subject: [PATCH 0032/1541] Fixed bug in interface_functions file. --- doc/interface_functions.aux | 47 +++++++++--------- doc/interface_functions.log | 96 ++++++++++++++++++++----------------- doc/interface_functions.tex | 2 +- doc/interface_functions.toc | 47 +++++++++--------- 4 files changed, 100 insertions(+), 92 deletions(-) diff --git a/doc/interface_functions.aux b/doc/interface_functions.aux index 53196f5c..45842c57 100644 --- a/doc/interface_functions.aux +++ b/doc/interface_functions.aux @@ -1,34 +1,35 @@ \relax -\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}} -\@writefile{toc}{\contentsline {section}{\numberline {2}Functions}{1}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Functions from \texttt {model.py}}{1}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.1}Core functions:}{1}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.2}Creating regions}{2}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{4}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{7}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{7}} +\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{2}} +\@writefile{toc}{\contentsline {section}{\numberline {2}Functions}{2}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Functions from \texttt {model.py}}{2}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.1}Core functions:}{2}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.2}Creating regions}{3}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{5}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{8}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{8}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.2}Handling objects}{8}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{8}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{9}} \@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{9}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{9}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{10}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.3}Get data}{10}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.4}Additional functions}{10}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.3}Get data}{11}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.4}Additional functions}{11}} \@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{11}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{11}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{13}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{14}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{14}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{14}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{15}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{15}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{15}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{19}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Notes on other files}{20}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{20}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{20}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{20}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{20}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{16}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{20}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Notes on other files}{21}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{21}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{21}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{21}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{21}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{21}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{21}} \@writefile{toc}{\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{21}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{21}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{21}} -\@writefile{toc}{\contentsline {section}{\numberline {3}Todo}{21}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{22}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{22}} +\@writefile{toc}{\contentsline {section}{\numberline {3}Todo}{22}} diff --git a/doc/interface_functions.log b/doc/interface_functions.log index 5099a1e6..6a8ef0df 100644 --- a/doc/interface_functions.log +++ b/doc/interface_functions.log @@ -1,4 +1,4 @@ -This is pdfTeXk, Version 3.141592-1.40.3 (Web2C 7.5.6) (format=pdflatex 2010.5.19) 13 MAY 2011 12:08 +This is pdfTeXk, Version 3.141592-1.40.3 (Web2C 7.5.6) (format=pdflatex 2010.5.19) 13 MAY 2011 14:04 entering extended mode %&-line parsing enabled. **interface_functions.tex @@ -66,10 +66,18 @@ LaTeX Font Info: External font `cmex10' loaded for size (Font) <8> on input line 10. LaTeX Font Info: External font `cmex10' loaded for size (Font) <6> on input line 10. - (./interface_functions.toc) + (./interface_functions.toc +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <7> on input line 3. +LaTeX Font Info: External font `cmex10' loaded for size +(Font) <5> on input line 3. +) \tf@toc=\write3 \openout3 = `interface_functions.toc'. + [1 + +{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <12> not available (Font) Font shape `OT1/cmtt/m/n' tried instead on input line 21. @@ -90,9 +98,7 @@ Overfull \hbox (22.4968pt too wide) in paragraph at lines 46--46 0, 0] and[] [] -[1 -{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] Overfull \hbox (17.24684pt too wide) in paragraph at lines 76--76 [] \OT1/cmtt/m/n/10 the radius for this Species in/on this Region or Surface.[] @@ -104,7 +110,7 @@ Overfull \hbox (17.24684pt too wide) in paragraph at lines 76--76 an exist.[] [] - +[2] Overfull \hbox (22.4968pt too wide) in paragraph at lines 76--76 [] \OT1/cmtt/m/n/10 Optional. If you do not specify a Structure the Species is[] @@ -122,7 +128,7 @@ Overfull \hbox (11.99689pt too wide) in paragraph at lines 76--76 urfaces,[] [] -[2] + Overfull \hbox (17.24684pt too wide) in paragraph at lines 164--164 [] \OT1/cmtt/m/n/10 the vector [x, y, z] from the corner closest to [0, 0, 0], to[] @@ -134,7 +140,7 @@ Overfull \hbox (22.4968pt too wide) in paragraph at lines 164--164 n, length)[] [] - +[3] Overfull \hbox (1.49698pt too wide) in paragraph at lines 164--164 [] \OT1/cmtt/m/n/10 the point [x, y, z] on the axis of the cylinder clos est to[] @@ -170,7 +176,7 @@ Overfull \hbox (27.74675pt too wide) in paragraph at lines 164--164 g the plane[] [] -[3] + Overfull \hbox (27.74675pt too wide) in paragraph at lines 164--164 [] \OT1/cmtt/m/n/10 the length of the plane along the unit vector unit_x . Should be[] @@ -182,7 +188,7 @@ Overfull \hbox (27.74675pt too wide) in paragraph at lines 164--164 . Should be[] [] - +[4] Overfull \hbox (11.99689pt too wide) in paragraph at lines 180--180 [] \OT1/cmtt/m/n/10 """Add a Structure (Region or Surface) to the Partic leModel.[] @@ -206,7 +212,7 @@ Overfull \hbox (1.49698pt too wide) in paragraph at lines 198--198 r each[] [] -[4] + Overfull \hbox (32.9967pt too wide) in paragraph at lines 222--222 [] \OT1/cmtt/m/n/10 reaction rate. Units: per second. (Rough order o f magnitude:[] @@ -218,7 +224,7 @@ Overfull \hbox (32.9967pt too wide) in paragraph at lines 222--222 all reaction[] [] - +[5] Overfull \hbox (32.9967pt too wide) in paragraph at lines 241--241 [] \OT1/cmtt/m/n/10 reaction rate. Units: per second. (Rough order o f magnitude:[] @@ -254,7 +260,7 @@ Overfull \hbox (17.24684pt too wide) in paragraph at lines 269--269 nation of[] [] -[5] + Overfull \hbox (1.49698pt too wide) in paragraph at lines 269--269 [] \OT1/cmtt/m/n/10 reactants for which no bimolecular reaction rule is spec ified.[] @@ -272,7 +278,7 @@ Overfull \hbox (11.99689pt too wide) in paragraph at lines 302--302 ct, ka):[] [] - +[6] Overfull \hbox (27.74675pt too wide) in paragraph at lines 302--302 [] \OT1/cmtt/m/n/10 intrinsic reaction rate. Units: meters^3 per sec ond. (Rough[] @@ -320,7 +326,7 @@ Overfull \hbox (27.74675pt too wide) in paragraph at lines 332--332 gh order of[] [] -[6] + Overfull \hbox (27.74675pt too wide) in paragraph at lines 332--332 [] \OT1/cmtt/m/n/10 overall reaction rate (koff) for this reaction rule to a n intrinsic[] @@ -332,7 +338,7 @@ Overfull \hbox (11.99689pt too wide) in paragraph at lines 332--332 , kD) or[] [] - +[7] Overfull \hbox (1.49698pt too wide) in paragraph at lines 378--378 [] \OT1/cmtt/m/n/10 The world object keeps track of the positions of the par ticles[] @@ -404,7 +410,7 @@ Overfull \hbox (17.24684pt too wide) in paragraph at lines 378--378 arger. If[] [] -[7] + Overfull \hbox (17.24684pt too wide) in paragraph at lines 378--378 [] \OT1/cmtt/m/n/10 you have more particles, you want a larger matrix_size, such that[] @@ -416,7 +422,7 @@ Overfull \hbox (1.49698pt too wide) in paragraph at lines 378--378 ber of[] [] - +[8] Overfull \hbox (1.49698pt too wide) in paragraph at lines 400--400 []\OT1/cmtt/m/n/10 def get_closest_surface_within_radius(world, pos, radius, ig nore):[] @@ -440,7 +446,7 @@ Overfull \hbox (22.4968pt too wide) in paragraph at lines 437--437 osition in[] [] -[8] + Overfull \hbox (38.24666pt too wide) in paragraph at lines 437--437 [] \OT1/cmtt/m/n/10 a position vector [x, y, z]. Units: [meters, met ers, meters].[] @@ -452,7 +458,7 @@ Overfull \hbox (6.74693pt too wide) in paragraph at lines 437--437 method[] [] - +[9] Overfull \hbox (11.99689pt too wide) in paragraph at lines 462--462 [] \OT1/cmtt/m/n/10 def __init__(self, world, rng=myrandom.rng, network_rule s=None):[] @@ -500,7 +506,7 @@ Overfull \hbox (27.74675pt too wide) in paragraph at lines 493--493 simulator:[] [] -[9] + Overfull \hbox (22.4968pt too wide) in paragraph at lines 493--493 [] \OT1/cmtt/m/n/10 This method is called stop because it is usually cal led at the[] @@ -530,7 +536,7 @@ Overfull \hbox (41.35085pt too wide) in paragraph at lines 494--495 cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] - +[10] Overfull \hbox (41.35085pt too wide) in paragraph at lines 505--506 \OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) @@ -554,13 +560,13 @@ Overfull \hbox (41.35085pt too wide) in paragraph at lines 518--519 cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] -[10] [11] +[11] Overfull \hbox (32.9967pt too wide) in paragraph at lines 626--626 [] \OT1/cmtt/m/n/10 """ Return a generator (using "yield") to loop over (pid , particle).[] [] - +[12] Overfull \hbox (17.24684pt too wide) in paragraph at lines 641--641 [] \OT1/cmtt/m/n/10 a Species or the name of a Species. If none is s pecified,[] @@ -578,13 +584,13 @@ Overfull \hbox (17.24684pt too wide) in paragraph at lines 656--656 pecified,[] [] -[12] + Overfull \hbox (27.74675pt too wide) in paragraph at lines 656--656 [] \OT1/cmtt/m/n/10 all (particle identifier, particle)-pairs will b e returned.[] [] - +[13] Overfull \hbox (27.74675pt too wide) in paragraph at lines 685--685 [] \OT1/cmtt/m/n/10 (Species name, number of particles)-pairs will b e returned.[] @@ -608,7 +614,7 @@ Overfull \hbox (27.74675pt too wide) in paragraph at lines 713--713 simulator.[] [] -[13] + Overfull \hbox (11.99689pt too wide) in paragraph at lines 742--742 [] \OT1/cmtt/m/n/10 """Return three lists with all the reaction rules define d in the[] @@ -620,7 +626,7 @@ Overfull \hbox (6.74693pt too wide) in paragraph at lines 742--742 on rate[] [] - +[14] Overfull \hbox (17.24684pt too wide) in paragraph at lines 750--750 [] \OT1/cmtt/m/n/10 ReactionRule.__str__ would be good, but we are actually getting a[] @@ -632,7 +638,7 @@ Overfull \hbox (1.49698pt too wide) in paragraph at lines 762--762 rules[] [] -[14] + Overfull \hbox (17.24684pt too wide) in paragraph at lines 801--801 [] \OT1/cmtt/m/n/10 """Return True if a and b are equal, subject to given to lerances.[] @@ -650,7 +656,7 @@ Overfull \hbox (27.74675pt too wide) in paragraph at lines 801--801 tolerances.[] [] - +[15] Overfull \hbox (11.99689pt too wide) in paragraph at lines 801--801 [] \OT1/cmtt/m/n/10 """Return True if a is less than b, subject to given tol erances.[] @@ -680,13 +686,13 @@ Overfull \hbox (32.9967pt too wide) in paragraph at lines 841--841 r second' to[] [] -[15] + Overfull \hbox (11.99689pt too wide) in paragraph at lines 841--841 [] \OT1/cmtt/m/n/10 """Convert a concentration from units 'micromolar' to un its 'per[] [] - +[16] Overfull \hbox (17.24684pt too wide) in paragraph at lines 996--996 [] \OT1/cmtt/m/n/10 """Calculate the 'pseudo-'reaction rate (kD) caused by d iffusion.[] @@ -728,13 +734,13 @@ Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 annihilation[] [] -[16] + Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 [] \OT1/cmtt/m/n/10 the overall reaction rate for the reaction rule. Units:[] [] - +[17] Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 [] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind ing reaction[] @@ -770,13 +776,13 @@ Overfull \hbox (11.99689pt too wide) in paragraph at lines 996--996 on rule.[] [] -[17] + Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 [] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind ing reaction[] [] - +[18] Overfull \hbox (17.24684pt too wide) in paragraph at lines 996--996 [] \OT1/cmtt/m/n/10 Similar to the function k_d(), but expects an intrinsic rate (ka)[] @@ -800,7 +806,7 @@ Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 ing reaction[] [] -[18] + Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 [] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (kd) for an unbind ing reaction[] @@ -812,22 +818,22 @@ Overfull \hbox (1.49698pt too wide) in paragraph at lines 996--996 c rate[] [] -[19] +[19] [20] LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <10> not available (Font) Font shape `OT1/cmtt/m/n' tried instead on input line 1056. - [20] + [21] Overfull \hbox (11.99689pt too wide) in paragraph at lines 1099--1099 [] \OT1/cmtt/m/n/10 def __init__(self, logname='log', directory='data', comm ent=''):[] [] -[21] (./interface_functions.aux) ) +[22] (./interface_functions.aux) ) Here is how much of TeX's memory you used: - 506 strings out of 95086 - 7733 string characters out of 1183255 - 66134 words of memory out of 1500000 - 3729 multiletter control sequences out of 10000+50000 + 508 strings out of 95086 + 7761 string characters out of 1183255 + 67134 words of memory out of 1500000 + 3731 multiletter control sequences out of 10000+50000 8578 words of font info for 31 fonts, out of 1200000 for 2000 28 hyphenation exceptions out of 8191 23i,6n,20p,323b,243s stack positions out of 5000i,500n,6000p,200000b,5000s @@ -838,9 +844,9 @@ e/fonts/type1/bluesky/cm/cmr12.pfb> -Output written on interface_functions.pdf (21 pages, 92315 bytes). +Output written on interface_functions.pdf (22 pages, 94420 bytes). PDF statistics: - 102 PDF objects out of 1000 (max. 8388607) + 105 PDF objects out of 1000 (max. 8388607) 0 named destinations out of 1000 (max. 131072) 1 words of extra memory for PDF output out of 10000 (max. 10000000) diff --git a/doc/interface_functions.tex b/doc/interface_functions.tex index c33e457f..f599b699 100644 --- a/doc/interface_functions.tex +++ b/doc/interface_functions.tex @@ -720,7 +720,7 @@ \subsubsection{Get information on domains} """ \end{verbatim} -subsubsection{Get information on reaction rules} +\subsubsection{Get information on reaction rules} \begin{verbatim} def get_reaction_rules(model_or_simulator): diff --git a/doc/interface_functions.toc b/doc/interface_functions.toc index 9131d2db..30f912cb 100644 --- a/doc/interface_functions.toc +++ b/doc/interface_functions.toc @@ -1,33 +1,34 @@ -\contentsline {section}{\numberline {1}Introduction}{1} -\contentsline {section}{\numberline {2}Functions}{1} -\contentsline {subsection}{\numberline {2.1}Functions from \texttt {model.py}}{1} -\contentsline {subsubsection}{\numberline {2.1.1}Core functions:}{1} -\contentsline {subsubsection}{\numberline {2.1.2}Creating regions}{2} -\contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{4} -\contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{7} -\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{7} +\contentsline {section}{\numberline {1}Introduction}{2} +\contentsline {section}{\numberline {2}Functions}{2} +\contentsline {subsection}{\numberline {2.1}Functions from \texttt {model.py}}{2} +\contentsline {subsubsection}{\numberline {2.1.1}Core functions:}{2} +\contentsline {subsubsection}{\numberline {2.1.2}Creating regions}{3} +\contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{5} +\contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{8} +\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{8} \contentsline {subsubsection}{\numberline {2.2.2}Handling objects}{8} -\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{8} +\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{9} \contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{9} \contentsline {subsubsection}{\numberline {2.3.1}Core functions}{9} \contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{10} -\contentsline {subsubsection}{\numberline {2.3.3}Get data}{10} -\contentsline {subsubsection}{\numberline {2.3.4}Additional functions}{10} +\contentsline {subsubsection}{\numberline {2.3.3}Get data}{11} +\contentsline {subsubsection}{\numberline {2.3.4}Additional functions}{11} \contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{11} \contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{11} -\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{13} -\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{14} +\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{14} +\contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{14} +\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{15} \contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{15} -\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{15} -\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{19} -\contentsline {subsection}{\numberline {2.6}Notes on other files}{20} -\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{20} -\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{20} -\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{20} -\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{20} +\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{16} +\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{20} +\contentsline {subsection}{\numberline {2.6}Notes on other files}{21} +\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{21} +\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{21} +\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{21} +\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{21} \contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{21} \contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{21} \contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{21} -\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{21} -\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{21} -\contentsline {section}{\numberline {3}Todo}{21} +\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{22} +\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{22} +\contentsline {section}{\numberline {3}Todo}{22} From fbe7e2b71e29006c7d6d6840ada2c5fb4ffd71b3 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Fri, 13 May 2011 18:09:01 +0200 Subject: [PATCH 0033/1541] Got samples/rebind/run.py running. HOWEVER, since it made use of some functions that are no longer in the code, I had to come up with some sort of substitutes. This code doesn't function properly in all cases. Namely: * The function EGFRDSimulator.distance_between_particles() doesn't exist anymore. Instead I get particle all particle IDs of a certain type. Then I simply take the first particle in the list to calculate the distance between hopefully the right particles. This thus only works when there are only two particles. I should really look into what we want to measure before fixing this. A possible solution would be to just save the ID of the particle that was placed initially with place_particle. * The function clear_volume gives the IDs of domains in a certain neighboorhood. It bursted these domains to get them up to the current time. But this leads to the problem that a particle might belong to a domain in the to be cleared area, but it's actual position is not in the cleared area. This might lead to an infinite loop. --- samples/rebind/run.py | 56 +++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/samples/rebind/run.py b/samples/rebind/run.py index 84b09a2b..54200d51 100644 --- a/samples/rebind/run.py +++ b/samples/rebind/run.py @@ -150,27 +150,51 @@ def singlerun(T_list, D_factor, N_B, N_X): area, repeat the process (in very crowded box, this leads to infinite loop) - + + Perhaps it would be more easy to just place all the particles at + the beginning, but give A and B initially a diffusion constant of + 0, then stirr, and then set the diffusion constant to their + desired values.. + """ + print "Started clearing.." while 1: - pp = s.get_particles_within_radius(A_pos, float(A['radius'])) - if not pp: + #TODO this code doesn't function properly, because you want to clear + # particles within a certain radius.. Now you can be removing particles + # that after bursting are not in the area anymore, but whose bursted + # domains were. + + # After bursting you should just check whether something is within the radius. + + #pp = s.get_particles_within_radius(A_pos, float(A['radius'])) + dd = s.clear_volume(A_pos, float(A['radius'])) # returns a list of + # domains ID's that + # where bursted within + # volume + if not dd: break - for p in pp: - s.remove_particle(p) + for d in dd: + for p in d.particle_id_pair: + s.remove_particle(p) s.throw_in_particles(X, len(pp), box1) - s.place_particle(A, A_pos) + place_particle(w, A, A_pos) # Idem for a particle B: while 1: - pp = s.get_particles_within_radius(B_pos, float(B['radius'])) - if not pp: + dd = s.clear_volume(B_pos, float(B['radius'])) # returns a list of + # domains ID's that + # where bursted within + # volume + if not dd: break - for p in pp: - s.remove_particle(p) - s.throw_in_particles(X, len(pp), box1) + for d in dd: + for p in d.particle_id_pair: + s.remove_particle(p) + s.throw_in_particles(X, len(pp), box1) - s.place_particle(B, B_pos) + place_particle(w, B, B_pos) + print "Finished clearing.." # Place rest of B at random positions if N_B > 1: @@ -215,8 +239,14 @@ def singlerun(T_list, D_factor, N_B, N_X): s.stop(next_stop) if len(s.world.get_particle_ids(C.id)) != 0: #A,B r_list.append(0) - else: - r_list.append(s.distance_between_particles(A.id, B.id)) + else: # C = 0, i.e. there are particles A and B + # r_list.append(s.distance_between_particles(A.id, B.id)) + # If there's only 1 B-particle + particlesA = w.get_particle_ids(A.id) + particlesB = w.get_particle_ids(B.id) + positionA = particlesA[1].position() + positionB = particlesB[1].position() + r_list.append(w.distance(positionA, positionB)) i_T += 1 next_stop = T_list[i_T] From e9136e0a293ebdc097dbd5edb8652e70d82bdbb6 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Mon, 16 May 2011 10:26:45 +0200 Subject: [PATCH 0034/1541] Sorry, another test indicated example still doesn't work. Apparently positionA = particlesA[1].position() isn't allowed. Didn't discover this earlier because didn't give array of stopping times as input, only "INF". But then the simulation isn't really allowed to run. ~MW --- samples/rebind/run.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/samples/rebind/run.py b/samples/rebind/run.py index 54200d51..7319b31a 100644 --- a/samples/rebind/run.py +++ b/samples/rebind/run.py @@ -28,8 +28,8 @@ def run(outfilename, D_factor, N_B, N_X, N): tau = sigma**2 / D_tot print 'tau=', tau - #T_list = [tau * .1, INF] - T_list = [INF] + T_list = [tau * .1, INF] + # T_list = [INF] # With this option, the simulation stops immediately outfile_t = open(outfilename + '_t.dat', 'w') #outfile_r_list = [open(outfilename + '_r_-1.dat', 'w')] @@ -176,6 +176,7 @@ def singlerun(T_list, D_factor, N_B, N_X): for d in dd: for p in d.particle_id_pair: s.remove_particle(p) + print "Removing particle.." s.throw_in_particles(X, len(pp), box1) place_particle(w, A, A_pos) @@ -191,10 +192,11 @@ def singlerun(T_list, D_factor, N_B, N_X): for d in dd: for p in d.particle_id_pair: s.remove_particle(p) + print "Removing particle.." s.throw_in_particles(X, len(pp), box1) place_particle(w, B, B_pos) - print "Finished clearing.." + print "Finished placing particles A & B.." # Place rest of B at random positions if N_B > 1: From c1fd3fc930f95b74f5c7e6d605b56610e2a72728 Mon Sep 17 00:00:00 2001 From: Laurens Bossen Date: Mon, 16 May 2011 13:23:22 +0200 Subject: [PATCH 0035/1541] added .gitignore to ignore autogenerated files --- .gitignore | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..cc6f271e --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# files that should be ignored by git for tracking +# +# ignore all compiled python files +*.pyc +# ignore all compiled C++ libraries and object files +*.lo +*.la +*.lai +*.so +*.Plo +*.Po +*.o +*.a +# ignore generated Bessel tables +/CylindricalBesselTable.hpp +/SphericalBesselTable.hpp + +# ignore files generated by pdflatex +*.aux +*.log +*.pdf + +# ignore Makefiles.in generated by automake and Makefiles generated by configure +Makefile.in +/Makefile +/doc/Makefile +/binding/Makefile +/samples/benchmark/Makefile +/test/Makefile + +# all kinds of configuration files follow now +# ignore files generated by configure +/config.h +/config.log +/config.status +/stamp-h1 +# ignore scripts generated by configure (I think) +/config.guess +/config.sub +/libtool +/ltmain.sh +# ignore files generated by autoconf +/configure +/depcomp +/missing +/py-compile +/install-sh +/m4/libtool.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/m4/lt~obsolete.m4 +/autom4te.cache/ +# ignore files generated by autoheader +/config.h.in +# ignore files generated by aclocal +/aclocal.m4 + From b0382ec0f0b0329a23f7c5aabae4f48a8f02ca2d Mon Sep 17 00:00:00 2001 From: Laurens Bossen Date: Mon, 16 May 2011 15:44:16 +0200 Subject: [PATCH 0036/1541] *.out and *.pyo files added to the gitignore list --- .gitignore | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cc6f271e..3dfdb44f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ -# files that should be ignored by git for tracking +# Files that should be ignored by git for tracking +# For explanation see the gitignore man page # # ignore all compiled python files *.pyc +*.pyo # ignore all compiled C++ libraries and object files *.lo *.la @@ -20,6 +22,9 @@ *.log *.pdf +# ignore data files generated by the examples +*.out + # ignore Makefiles.in generated by automake and Makefiles generated by configure Makefile.in /Makefile From 0bdb716d38ad6ae1faf7e1aa136d10def10581d2 Mon Sep 17 00:00:00 2001 From: Laurens Bossen Date: Mon, 16 May 2011 15:50:11 +0200 Subject: [PATCH 0037/1541] add execute permissions on samples python script for easier launching --- samples/reversible/make_p_rev_files.py | 0 samples/reversible/p_rev.py | 0 samples/reversible/plot.py | 0 samples/reversible/run.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 samples/reversible/make_p_rev_files.py mode change 100644 => 100755 samples/reversible/p_rev.py mode change 100644 => 100755 samples/reversible/plot.py mode change 100644 => 100755 samples/reversible/run.py diff --git a/samples/reversible/make_p_rev_files.py b/samples/reversible/make_p_rev_files.py old mode 100644 new mode 100755 diff --git a/samples/reversible/p_rev.py b/samples/reversible/p_rev.py old mode 100644 new mode 100755 diff --git a/samples/reversible/plot.py b/samples/reversible/plot.py old mode 100644 new mode 100755 diff --git a/samples/reversible/run.py b/samples/reversible/run.py old mode 100644 new mode 100755 From 30c3566fad13257f5c3706df35c9cfba9b726fc0 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Mon, 16 May 2011 16:50:31 +0200 Subject: [PATCH 0038/1541] Mainly documentation update. While working on ../rebind/run.py ran into some functions I overlooked in gfrdbase.py. Thus added some functions to /doc/interface_functions.tex. Also, some convenient functions didn't have a docstring yet. Also added those. I've marked all docstrings I've added with: \#TODO: Added by wehrens@amolf.nl; please revise So, as the comment already said, please revise these docstrings. More info on /rebind will follow in next commit. ~MW --- doc/interface_functions.aux | 55 +++++---- doc/interface_functions.log | 220 ++++++++++++++++++++---------------- doc/interface_functions.tex | 126 ++++++++++++++++++++- doc/interface_functions.toc | 55 +++++---- gfrdbase.py | 49 ++++++++ samples/rebind/run.py | 145 +++++++++++++++--------- 6 files changed, 448 insertions(+), 202 deletions(-) diff --git a/doc/interface_functions.aux b/doc/interface_functions.aux index 45842c57..37ee8f85 100644 --- a/doc/interface_functions.aux +++ b/doc/interface_functions.aux @@ -7,29 +7,34 @@ \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{5}} \@writefile{toc}{\contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{8}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{8}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.2}Handling objects}{8}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.2}Handling objects (surfaces, cylinders, etc)}{9}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{9}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{9}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{9}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{10}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.3}Get data}{11}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.4}Additional functions}{11}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{11}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{11}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{14}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{14}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{15}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{15}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{16}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{20}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Notes on other files}{21}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{21}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{21}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{21}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{21}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{21}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{21}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{21}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{22}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{22}} -\@writefile{toc}{\contentsline {section}{\numberline {3}Todo}{22}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.4}Obtaining particle information}{10}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{10}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{10}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{11}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.3}Get data}{12}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.4}Get position and distance data}{12}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.5}Additional functions}{13}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{13}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{13}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{16}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{16}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{17}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{17}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{17}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{21}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Notes on other files}{22}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{22}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{22}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{22}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{23}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{23}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{23}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{23}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{23}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{23}} +\@writefile{toc}{\contentsline {section}{\numberline {3}Todo}{23}} +\@writefile{toc}{\contentsline {section}{\numberline {4}On C++ data structures}{24}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}SpeciesInfo}{24}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Particle}{24}} diff --git a/doc/interface_functions.log b/doc/interface_functions.log index 6a8ef0df..a8cbf220 100644 --- a/doc/interface_functions.log +++ b/doc/interface_functions.log @@ -1,4 +1,4 @@ -This is pdfTeXk, Version 3.141592-1.40.3 (Web2C 7.5.6) (format=pdflatex 2010.5.19) 13 MAY 2011 14:04 +This is pdfTeXk, Version 3.141592-1.40.3 (Web2C 7.5.6) (format=pdflatex 2010.5.19) 16 MAY 2011 16:26 entering extended mode %&-line parsing enabled. **interface_functions.tex @@ -71,13 +71,12 @@ LaTeX Font Info: External font `cmex10' loaded for size (Font) <7> on input line 3. LaTeX Font Info: External font `cmex10' loaded for size (Font) <5> on input line 3. -) + [1 + +{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}]) \tf@toc=\write3 \openout3 = `interface_functions.toc'. - [1 - -{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <12> not available (Font) Font shape `OT1/cmtt/m/n' tried instead on input line 21. @@ -98,7 +97,7 @@ Overfull \hbox (22.4968pt too wide) in paragraph at lines 46--46 0, 0] and[] [] - +[2] Overfull \hbox (17.24684pt too wide) in paragraph at lines 76--76 [] \OT1/cmtt/m/n/10 the radius for this Species in/on this Region or Surface.[] @@ -110,7 +109,7 @@ Overfull \hbox (17.24684pt too wide) in paragraph at lines 76--76 an exist.[] [] -[2] + Overfull \hbox (22.4968pt too wide) in paragraph at lines 76--76 [] \OT1/cmtt/m/n/10 Optional. If you do not specify a Structure the Species is[] @@ -134,13 +133,13 @@ Overfull \hbox (17.24684pt too wide) in paragraph at lines 164--164 0, 0], to[] [] - +[3] Overfull \hbox (22.4968pt too wide) in paragraph at lines 164--164 []\OT1/cmtt/m/n/10 """create_cylindrical_surface(id, corner, radius, orientatio n, length)[] [] -[3] + Overfull \hbox (1.49698pt too wide) in paragraph at lines 164--164 [] \OT1/cmtt/m/n/10 the point [x, y, z] on the axis of the cylinder clos est to[] @@ -332,13 +331,13 @@ Overfull \hbox (27.74675pt too wide) in paragraph at lines 332--332 n intrinsic[] [] - +[7] Overfull \hbox (11.99689pt too wide) in paragraph at lines 332--332 [] \OT1/cmtt/m/n/10 reaction rate (kd) with the function utils.k_d(koff, kon , kD) or[] [] -[7] + Overfull \hbox (1.49698pt too wide) in paragraph at lines 378--378 [] \OT1/cmtt/m/n/10 The world object keeps track of the positions of the par ticles[] @@ -459,376 +458,395 @@ Overfull \hbox (6.74693pt too wide) in paragraph at lines 437--437 [] [9] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 462--462 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 505--505 [] \OT1/cmtt/m/n/10 def __init__(self, world, rng=myrandom.rng, network_rule s=None):[] [] - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 462--462 +[10] +Overfull \hbox (17.24684pt too wide) in paragraph at lines 505--505 [] \OT1/cmtt/m/n/10 a random number generator. By default myrand om.rng is[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 462--462 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 505--505 [] \OT1/cmtt/m/n/10 used, which uses Mersenne Twister from the G SL library.[] [] -Overfull \hbox (53.99652pt too wide) in paragraph at lines 462--462 +Overfull \hbox (53.99652pt too wide) in paragraph at lines 505--505 [] \OT1/cmtt/m/n/10 you don't need to use this, for backward com patibility only.[] [] -Overfull \hbox (41.35085pt too wide) in paragraph at lines 470--471 +Overfull \hbox (41.35085pt too wide) in paragraph at lines 513--514 \OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 493--493 +Overfull \hbox (17.24684pt too wide) in paragraph at lines 536--536 [] \OT1/cmtt/m/n/10 asynchronously. This method bursts all protective do mains and[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 493--493 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 536--536 [] \OT1/cmtt/m/n/10 the time at which to synchronize the particl es. Usually[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 493--493 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 536--536 [] \OT1/cmtt/m/n/10 you will want to use the current time of the simulator:[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 493--493 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 536--536 [] \OT1/cmtt/m/n/10 This method is called stop because it is usually cal led at the[] [] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 493--493 +Overfull \hbox (17.24684pt too wide) in paragraph at lines 536--536 [] \OT1/cmtt/m/n/10 end of a simulation. It is possible to call this met hod at an[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 493--493 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 536--536 [] \OT1/cmtt/m/n/10 earlier time. For example the Logger module does thi s, because[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 493--493 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 536--536 [] \OT1/cmtt/m/n/10 it needs to know the positions of the particles at e ach log[] [] -Overfull \hbox (41.35085pt too wide) in paragraph at lines 494--495 +Overfull \hbox (41.35085pt too wide) in paragraph at lines 537--538 \OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] -[10] -Overfull \hbox (41.35085pt too wide) in paragraph at lines 505--506 + +Overfull \hbox (41.35085pt too wide) in paragraph at lines 548--549 \OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 517--517 +[11] +Overfull \hbox (6.74693pt too wide) in paragraph at lines 560--560 [] \OT1/cmtt/m/n/10 This function resets the "records" of the simulator. Thi s means[] [] -Overfull \hbox (1.49698pt too wide) in paragraph at lines 517--517 +Overfull \hbox (1.49698pt too wide) in paragraph at lines 560--560 [] \OT1/cmtt/m/n/10 the simulator time is reset, the step counter is reset, events[] [] -Overfull \hbox (41.35085pt too wide) in paragraph at lines 518--519 +Overfull \hbox (41.35085pt too wide) in paragraph at lines 561--562 \OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] -[11] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 626--626 + +Overfull \hbox (1.49698pt too wide) in paragraph at lines 605--605 +[]\OT1/cmtt/m/n/10 def get_closest_surface_within_radius(world, pos, radius, ig +nore):[] + [] + +[12] [13] +Overfull \hbox (32.9967pt too wide) in paragraph at lines 704--704 [] \OT1/cmtt/m/n/10 """ Return a generator (using "yield") to loop over (pid , particle).[] [] -[12] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 641--641 + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 719--719 [] \OT1/cmtt/m/n/10 a Species or the name of a Species. If none is s pecified,[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 641--641 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 719--719 [] \OT1/cmtt/m/n/10 all (particle identifier, particle)-pairs will b e returned.[] [] - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 656--656 +[14] +Overfull \hbox (17.24684pt too wide) in paragraph at lines 734--734 [] \OT1/cmtt/m/n/10 a Species or the name of a Species. If none is s pecified,[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 656--656 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 734--734 [] \OT1/cmtt/m/n/10 all (particle identifier, particle)-pairs will b e returned.[] [] -[13] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 685--685 + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 763--763 [] \OT1/cmtt/m/n/10 (Species name, number of particles)-pairs will b e returned.[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 701--701 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 779--779 [] \OT1/cmtt/m/n/10 """Return a string containing the number of particles of a certain[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 701--701 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 779--779 [] \OT1/cmtt/m/n/10 a string of (Species name, number of particles)- pairs will[] [] - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 713--713 +[15] +Overfull \hbox (27.74675pt too wide) in paragraph at lines 791--791 [] \OT1/cmtt/m/n/10 """Return an iterator over the protective domains in the simulator.[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 742--742 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 820--820 [] \OT1/cmtt/m/n/10 """Return three lists with all the reaction rules define d in the[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 742--742 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 820--820 [] \OT1/cmtt/m/n/10 - reaction rules between two reactants with a reacti on rate[] [] -[14] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 750--750 + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 828--828 [] \OT1/cmtt/m/n/10 ReactionRule.__str__ would be good, but we are actually getting a[] [] -Overfull \hbox (1.49698pt too wide) in paragraph at lines 762--762 +Overfull \hbox (1.49698pt too wide) in paragraph at lines 840--840 [] \OT1/cmtt/m/n/10 """Return a formatted string containing all the reaction rules[] [] - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 801--801 +[16] +Overfull \hbox (17.24684pt too wide) in paragraph at lines 879--879 [] \OT1/cmtt/m/n/10 """Return True if a and b are equal, subject to given to lerances.[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 801--801 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 879--879 [] \OT1/cmtt/m/n/10 relative tolerance multipied by this typical value, and will be[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 801--801 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 879--879 [] \OT1/cmtt/m/n/10 """Return True if a is greater than b, subject to given tolerances.[] [] -[15] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 801--801 + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 879--879 [] \OT1/cmtt/m/n/10 """Return True if a is less than b, subject to given tol erances.[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 801--801 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 879--879 [] \OT1/cmtt/m/n/10 """Return True if a is greater or equal than b, subject to given[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 801--801 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 879--879 [] \OT1/cmtt/m/n/10 """Return True if a is less than or equal than b, subjec t to given[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 841--841 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 919--919 [] \OT1/cmtt/m/n/10 """Convert a reaction rate from units 'per molar per sec ond' to[] [] - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 841--841 +[17] +Overfull \hbox (32.9967pt too wide) in paragraph at lines 919--919 [] \OT1/cmtt/m/n/10 """Convert a reaction rate from units 'per micromolar pe r second' to[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 841--841 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 919--919 [] \OT1/cmtt/m/n/10 """Convert a concentration from units 'micromolar' to un its 'per[] [] -[16] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 996--996 + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 """Calculate the 'pseudo-'reaction rate (kD) caused by d iffusion.[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 996--996 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 kD is equal to 1 divided by the time it takes for two pa rticles to[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 meet each other by diffusion. It is needed when converti ng from[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 an intrinsic reaction rate to an overall reaction rates or vice[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 the diffusion constant of particle A plus the di ffusion[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 the radius of particle A plus the radius of part icle B.[] [] - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 +[18] +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (kon) for a binding/ annihilation[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 996--996 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 the overall reaction rate for the reaction rule. Units:[] [] -[17] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind ing reaction[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 996--996 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 This one is a bit tricky. We consider reaction rules wit h only 1[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 996--996 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 reactant. In case there is only 1 product also, no conve rsion in[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 996--996 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 necessary. But when the reaction rule has 2 products, we need to[] [] - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 996--996 +[19] +Overfull \hbox (22.4968pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 the overall reaction rate for the unbinding reac tion rule.[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 996--996 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 the overall reaction rate for the reverse reacti on rule.[] [] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind ing reaction[] [] -[18] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 996--996 + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 Similar to the function k_d(), but expects an intrinsic rate (ka)[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 996--996 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 instead of an overall rate (kon) for the reversed reacti on rule as[] [] -Overfull \hbox (38.24666pt too wide) in paragraph at lines 996--996 +Overfull \hbox (38.24666pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (ka) for a binding /annihilation[] [] - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 +[20] +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (kd) for an unbind ing reaction[] [] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 996--996 +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (kd) for an unbind ing reaction[] [] -Overfull \hbox (1.49698pt too wide) in paragraph at lines 996--996 +Overfull \hbox (1.49698pt too wide) in paragraph at lines 1074--1074 [] \OT1/cmtt/m/n/10 Similar to the function k_off(), but expects an intrinsi c rate[] [] -[19] [20] +[21] LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <10> not available -(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 1056. +(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 1134. - [21] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1099--1099 + [22] +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1177--1177 [] \OT1/cmtt/m/n/10 def __init__(self, logname='log', directory='data', comm ent=''):[] [] -[22] (./interface_functions.aux) ) +[23] +Overfull \hbox (374.24373pt too wide) in paragraph at lines 1216--1216 +[] \OT1/cmtt/m/n/10 SpeciesInfo (identifier_type const &id, D_type const &D=0. +, length_type const &r=0., structure_id_type const &s="", v_type const &v=0.)[] + + [] + + +Overfull \hbox (64.49643pt too wide) in paragraph at lines 1216--1216 +[]\OT1/cmtt/m/n/10 template[] + [] + +[24] (./interface_functions.aux) ) Here is how much of TeX's memory you used: 508 strings out of 95086 7761 string characters out of 1183255 @@ -836,17 +854,19 @@ Here is how much of TeX's memory you used: 3731 multiletter control sequences out of 10000+50000 8578 words of font info for 31 fonts, out of 1200000 for 2000 28 hyphenation exceptions out of 8191 - 23i,6n,20p,323b,243s stack positions out of 5000i,500n,6000p,200000b,5000s + 23i,8n,20p,323b,243s stack positions out of 5000i,500n,6000p,200000b,5000s -Output written on interface_functions.pdf (22 pages, 94420 bytes). +y/cm/cmr17.pfb> +Output written on interface_functions.pdf (24 pages, 109202 bytes). PDF statistics: - 105 PDF objects out of 1000 (max. 8388607) + 123 PDF objects out of 1000 (max. 8388607) 0 named destinations out of 1000 (max. 131072) 1 words of extra memory for PDF output out of 10000 (max. 10000000) diff --git a/doc/interface_functions.tex b/doc/interface_functions.tex index f599b699..5a61b356 100644 --- a/doc/interface_functions.tex +++ b/doc/interface_functions.tex @@ -378,7 +378,7 @@ \subsubsection{Core functions} \end{verbatim} -\subsubsection{Handling objects} +\subsubsection{Handling objects (surfaces, cylinders, etc)} \begin{verbatim} def get_closest_surface(world, pos, ignore): @@ -436,6 +436,68 @@ \subsubsection{Adding particles} """ \end{verbatim} +\subsubsection{Obtaining information about particles and species} + +\begin{verbatim} +\begin{verbatim} + + def get_species(self): + """ + Return an iterator over the Species in the simulator. + To be exact, it returns an iterator that returns + all SpeciesInfo instances defined in the simulator. + + Arguments: + - sim an EGFRDSimulator. + + More on output: + - SpeciesInfo is defined in the C++ code, and has + the following attributes that might be of interest + to the user: + * SpeciesInfo.id + * SpeciesInfo.radius + * SpeciesInfo.structure_id + * SpeciesInfo.D + * SpeciesInfo.v + + Example: + + for species in s.get_species(): + print str(species.radius) + + Note: + Dumper.py also holds a copy of this function. + + """ + + def get_first_pid(self, sid): + """ + Returns the first particle ID of a certain species. + + Arguments: + - sid: a(n) (eGFRD) simulator. + """ + + def get_position(self, object): + """ + Function that returns particle position. Can take multiple + sort of arguments as input. + + Arguments: + - object: can be: + * pid_particle_pair tuple + (of which pid_particle_pair[0] is _gfrd.ParticleID). + * An instance of _gfrd.ParticleID. + + * An instance of _gfrd.Particle. + * An instance of _gfrd.SpeciesID. + In the two latter cases, the function returns the + first ID of the position of the first particle of + the given species. + """ + +\end{verbatim} + \subsection{Functions from \texttt{egfrd.py}} \subsubsection{Core functions} @@ -518,7 +580,9 @@ \subsubsection{Simulator time (manipulation) functions} (Function belongs to EGFRDSimulator class, call with \texttt{EGFRDSimulator.get\_next\_time}.) \subsubsection{Get data} + \begin{verbatim} + def print_report(self, out=None): """Print various statistics about the simulation. @@ -526,8 +590,31 @@ \subsubsection{Get data} - None """ + +\end{verbatim} + +\subsubsection{Get position and distance data} +\begin{verbatim} +def get_closest_surface(world, pos, ignore): + """Return + - closest surface + - distance to closest surface + + We can not use matrix_space, it would miss a surface if the + origin of the surface would not be in the same or neighboring + cells as pos.""" + +def get_closest_surface_within_radius(world, pos, radius, ignore): + """Return: + - surface within radius or None + - closest surface (regardless of radius) + - distance to closest surface""" + + \end{verbatim} + + \subsubsection{Additional functions} The class \texttt{EGFRDSimulator} contains several functions which might be usefull to eGFRD users in very specific cases. Because of their specific nature, they don't contain docstrings. \begin{verbatim} @@ -1103,5 +1190,42 @@ \subsubsection{"Normal" logger} \section{Todo} - \texttt{sid = identifier.id} gives the sid, but what exactly is identifier? +- Updating following text (On C++ data structures).. + +\section{On C++ data structures} + +\subsection{SpeciesInfo} + +The information on species can be found in object created in the C++ code. Some Python functions return the SpeciesInfo object, which has the following attributes\footnote{Data extracted from Doxygen files.}: + +\begin{verbatim} +Public Types +typedef Tid_ identifier_type +typedef TD_ D_type +typedef TD_ v_type +typedef Tlen_ length_type +typedef Tstructure_id_ structure_id_type + +Public Member Functions +identifier_type const & id () const +length_type const & radius () const +length_type & radius () +structure_id_type const & structure_id () const +structure_id_type & structure_id () +D_type const & D () const +D_type & D () +v_type const & v () const +v_type & v () +bool operator== (SpeciesInfo const &rhs) const +bool operator!= (SpeciesInfo const &rhs) const + SpeciesInfo (identifier_type const &id, D_type const &D=0., length_type const &r=0., structure_id_type const &s="", v_type const &v=0.) + +template +struct SpeciesInfo< Tid_, TD_, Tlen_, Tstructure_id_ > +\end{verbatim} + +\subsection{Particle} + +The particle information is held by a class in the C++ code. A single particle is \end{document} diff --git a/doc/interface_functions.toc b/doc/interface_functions.toc index 30f912cb..9ac91ea3 100644 --- a/doc/interface_functions.toc +++ b/doc/interface_functions.toc @@ -6,29 +6,34 @@ \contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{5} \contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{8} \contentsline {subsubsection}{\numberline {2.2.1}Core functions}{8} -\contentsline {subsubsection}{\numberline {2.2.2}Handling objects}{8} +\contentsline {subsubsection}{\numberline {2.2.2}Handling objects (surfaces, cylinders, etc)}{9} \contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{9} -\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{9} -\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{9} -\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{10} -\contentsline {subsubsection}{\numberline {2.3.3}Get data}{11} -\contentsline {subsubsection}{\numberline {2.3.4}Additional functions}{11} -\contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{11} -\contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{11} -\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{14} -\contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{14} -\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{15} -\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{15} -\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{16} -\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{20} -\contentsline {subsection}{\numberline {2.6}Notes on other files}{21} -\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{21} -\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{21} -\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{21} -\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{21} -\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{21} -\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{21} -\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{21} -\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{22} -\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{22} -\contentsline {section}{\numberline {3}Todo}{22} +\contentsline {subsubsection}{\numberline {2.2.4}Obtaining particle information}{10} +\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{10} +\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{10} +\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{11} +\contentsline {subsubsection}{\numberline {2.3.3}Get data}{12} +\contentsline {subsubsection}{\numberline {2.3.4}Get position and distance data}{12} +\contentsline {subsubsection}{\numberline {2.3.5}Additional functions}{13} +\contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{13} +\contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{13} +\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{16} +\contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{16} +\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{17} +\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{17} +\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{17} +\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{21} +\contentsline {subsection}{\numberline {2.6}Notes on other files}{22} +\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{22} +\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{22} +\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{22} +\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{23} +\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{23} +\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{23} +\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{23} +\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{23} +\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{23} +\contentsline {section}{\numberline {3}Todo}{23} +\contentsline {section}{\numberline {4}On C++ data structures}{24} +\contentsline {subsection}{\numberline {4.1}SpeciesInfo}{24} +\contentsline {subsection}{\numberline {4.2}Particle}{24} diff --git a/gfrdbase.py b/gfrdbase.py index d62c038b..9d46ee09 100644 --- a/gfrdbase.py +++ b/gfrdbase.py @@ -310,12 +310,61 @@ def initialize(self): pass def get_species(self): + """ + Return an iterator over the Species in the simulator. + To be exact, it returns an iterator that returns + all SpeciesInfo instances defined in the simulator. + + Arguments: + - sim an EGFRDSimulator. + + More on output: + - SpeciesInfo is defined in the C++ code, and has + the following attributes that might be of interest + to the user: + * SpeciesInfo.id + * SpeciesInfo.radius + * SpeciesInfo.structure_id + * SpeciesInfo.D + * SpeciesInfo.v + + Example: + + for species in s.get_species(): + print str(species.radius) + + Note: + Dumper.py also holds a copy of this function. + + """ #TODO: Added by wehrens@amolf.nl; please revise return self.world.species def get_first_pid(self, sid): + """ + Returns the first particle ID of a certain species. + + Arguments: + - sid: a(n) (eGFRD) simulator. + """ #TODO: Added by wehrens@amolf.nl; please revise return iter(self.world.get_particle_ids(sid)).next() def get_position(self, object): + """ + Function that returns particle position. Can take multiple + sort of arguments as input. + + Arguments: + - object: can be: + * pid_particle_pair tuple + (of which pid_particle_pair[0] is _gfrd.ParticleID). + * An instance of _gfrd.ParticleID. + + * An instance of _gfrd.Particle. + * An instance of _gfrd.SpeciesID. + In the two latter cases, the function returns the + first ID of the position of the first particle of + the given species. + """ #TODO: Added by wehrens@amolf.nl; please revise if type(object) is tuple and type(object[0]) is _gfrd.ParticleID: pid = object[0] elif type(object) is _gfrd.ParticleID: diff --git a/samples/rebind/run.py b/samples/rebind/run.py index 7319b31a..057ed525 100644 --- a/samples/rebind/run.py +++ b/samples/rebind/run.py @@ -18,75 +18,87 @@ import model def run(outfilename, D_factor, N_B, N_X, N): - print outfilename + """ + Function that loops over single runs of binding/rebinding + simulations. (Plus some overhead code.) + Arguments: + - + + """ #TODO + global DX_factor, V, L, matrix_size + + print "Printing output to: " + outfilename + + ################# Set some constants radius = 2.5e-9 sigma = radius * 2 D = 1e-12 D_tot = D * 2 tau = sigma**2 / D_tot - print 'tau=', tau + DX_factor = 1 + + V = 1e-18 # m^3 + L = V ** (1.0/3.0) + + kf = 0.092e-18 + + DX = D * DX_factor + + matrix_size = min(max(3, int((9 * (N_X+N_B)) ** (1.0/3.0))), 60) + print 'matrix_size=', matrix_size + + # This is the list with steps T_list = [tau * .1, INF] - # T_list = [INF] # With this option, the simulation stops immediately + # Clear the distance output file + outfile_r = open(outfilename + '_r_-'+j+'.dat', 'a') + outfile_r.close() + + # Open the time output file outfile_t = open(outfilename + '_t.dat', 'w') - #outfile_r_list = [open(outfilename + '_r_-1.dat', 'w')] + ################# Loop through the multiple (N) simulations runs for i in range(N): + + # Perform a single simulation run r_list, t_list = singlerun(T_list, D_factor, N_B, N_X) + # Write away time data for t in t_list: outfile_t.write('%g\n' % t) - - #for j in range(len(r_list)): - # outfile_r_list[j].write('%g\n' % r_list[j]) - + + # Write away distance data, write data of different + # measurement points to different files. (Ending up + # with a list of distances corresponding to measure- + # ments at times from T_list.) + for j in range(len(r_list)): + outfile_r = open(outfilename + '_r_-'+j+'.dat', 'a') + outfile_r.write('%g\n' % r_list[j]) + outfile_r.close() + + # Print to screen. print i, r_list, t_list - outfile_t.flush() - #[outfile_r.flush() for outfile_r in outfile_r_list] + # Make sure script writes data NOW + outfile_t.flush() + # Close time output file outfile_t.close() - #[outfile_r.close() for outfile_r in outfile_r_list] - - def singlerun(T_list, D_factor, N_B, N_X): + """ + Function that performs one simulation run of binding/ + rebinding. - # 100 nM = 100e-9 * N_A * 100 / m^3 = 6.02e19 - # V = 1 / 6.02e19 = 1.66e-20 m^3 - # L = 2.55e-7 m - - # 1 uM = 6.02e20 / m^3 - # V = 1.66e-21 m^3 - # L = 1.18e-7 - - # ### Define constants - DX_factor = 1 - - V = 1e-18 # m^3 - L = V ** (1.0/3.0) - - matrix_size = min(max(3, int((9 * (N_X+N_B)) ** (1.0/3.0))), 60) - print 'matrix_size=', matrix_size - - radius = 2.5e-9 - sigma = radius * 2 - r0 = sigma - D = 1e-12 * D_factor - D_tot = D * 2 - - tau = sigma**2 / D_tot + Arguments: + - - #kf = 1000 * sigma * D_tot - # 1e9 [1 / (M s)] -> 1e9 / 1000 / N_A [m^3 / s] - kf = 0.092e-18 - - DX = D * DX_factor + """ #TODO - # ### Define model + ################# Define the model: # Create model class m = model.ParticleModel(L) @@ -108,13 +120,13 @@ def singlerun(T_list, D_factor, N_B, N_X): r2 = model.create_unbinding_reaction_rule(C, A, B, 1e3) m.network_rules.add_reaction_rule(r2) - # ### Set up simulator + ################# Set up simulator w = gfrdbase.create_world(m, matrix_size) nrw = gfrdbase.create_network_rules_wrapper(m) s = EGFRDSimulator(w, myrandom.rng, nrw) - # ### Place particles + ################# Place particles # Throw in some X particles and stirr if N_X != 0: @@ -128,14 +140,10 @@ def singlerun(T_list, D_factor, N_B, N_X): s.stop(end_time) break - + # Reset simulation, so stirring has no effect on output data s.reset() - # Removed introduction of network rules here. - # s.set_model(m) # IF presence of network rules results in data - # that is not cleaned in reset, this might give - # problems. Originally, s.set_model(m) would - # have introduced these rules here. + # Define positions for particles to place. A_pos = [0,0,0] B_pos = [(float(A['radius']) + float(B['radius']))+1e-23,0,0] @@ -235,12 +243,15 @@ def singlerun(T_list, D_factor, N_B, N_X): print 'reaction: ', s.t - t_last t_list.append(s.t - t_last) + """ If it's time to log, then log distance between particles. """ next_time = s.get_next_time() if next_time > next_stop: print 'stop', i_T, next_stop s.stop(next_stop) if len(s.world.get_particle_ids(C.id)) != 0: #A,B r_list.append(0) + # If particles are manifested as A and B, log distance + # inbetween. else: # C = 0, i.e. there are particles A and B # r_list.append(s.distance_between_particles(A.id, B.id)) # If there's only 1 B-particle @@ -248,11 +259,16 @@ def singlerun(T_list, D_factor, N_B, N_X): particlesB = w.get_particle_ids(B.id) positionA = particlesA[1].position() positionB = particlesB[1].position() + + particle = self.sim.world.get_particle(particle_id)[1] + r_list.append(w.distance(positionA, positionB)) i_T += 1 next_stop = T_list[i_T] + """ If there are no measuring moments on the list any more, + stop """ if next_stop == INF and len(t_list) != 0: print 'break', s.t break @@ -270,3 +286,30 @@ def singlerun(T_list, D_factor, N_B, N_X): '_' #+ os.environ['SGE_TASK_ID'] run(outfilename, float(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]), int(sys.argv[4])) + + + + +""" + +Some additional notes: + + # 100 nM = 100e-9 * N_A * 100 / m^3 = 6.02e19 + # V = 1 / 6.02e19 = 1.66e-20 m^3 + # L = 2.55e-7 m + + # 1 uM = 6.02e20 / m^3 + # V = 1.66e-21 m^3 + # L = 1.18e-7 + +""" + + + + + + + + + + From e65817df3722e2e6f787082c6d386ebb82beaf9d Mon Sep 17 00:00:00 2001 From: Laurens Bossen Date: Mon, 16 May 2011 17:49:48 +0200 Subject: [PATCH 0039/1541] add execute permission on the sample python scripts for easy use --- samples/dimer/dimer.py | 0 samples/hardbody/plot.py | 0 samples/hardbody/run_all.py | 0 samples/hardbody/run_single.py | 0 samples/irreversible/p_irr.py | 0 samples/irreversible/plot.py | 0 samples/irreversible/run.py | 0 7 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 samples/dimer/dimer.py mode change 100644 => 100755 samples/hardbody/plot.py mode change 100644 => 100755 samples/hardbody/run_all.py mode change 100644 => 100755 samples/hardbody/run_single.py mode change 100644 => 100755 samples/irreversible/p_irr.py mode change 100644 => 100755 samples/irreversible/plot.py mode change 100644 => 100755 samples/irreversible/run.py diff --git a/samples/dimer/dimer.py b/samples/dimer/dimer.py old mode 100644 new mode 100755 diff --git a/samples/hardbody/plot.py b/samples/hardbody/plot.py old mode 100644 new mode 100755 diff --git a/samples/hardbody/run_all.py b/samples/hardbody/run_all.py old mode 100644 new mode 100755 diff --git a/samples/hardbody/run_single.py b/samples/hardbody/run_single.py old mode 100644 new mode 100755 diff --git a/samples/irreversible/p_irr.py b/samples/irreversible/p_irr.py old mode 100644 new mode 100755 diff --git a/samples/irreversible/plot.py b/samples/irreversible/plot.py old mode 100644 new mode 100755 diff --git a/samples/irreversible/run.py b/samples/irreversible/run.py old mode 100644 new mode 100755 From ab52a88da1fdbb874141253c626719f227cec613 Mon Sep 17 00:00:00 2001 From: Thomas Sokolowski Date: Mon, 16 May 2011 18:30:44 +0200 Subject: [PATCH 0040/1541] Fixed order of args in on of the 1D-GF constructors. ... and made some minor changes to pair.py. --- GreensFunction1DAbsAbs.hpp | 12 +++--------- GreensFunction1DRadAbs.hpp | 8 ++++---- pair.py | 5 ++--- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/GreensFunction1DAbsAbs.hpp b/GreensFunction1DAbsAbs.hpp index 0449e966..78be41d2 100644 --- a/GreensFunction1DAbsAbs.hpp +++ b/GreensFunction1DAbsAbs.hpp @@ -1,5 +1,5 @@ -#if !defined( __FIRSTPASSAGEGREENSFUNCTION1D_HPP ) -#define __FIRSTPASSAGEGREENSFUNCTION1D_HPP +#if !defined( __GREENSFUNCTION1DABSABS_HPP ) +#define __GREENSFUNCTION1DABSABS_HPP #include #include @@ -42,12 +42,6 @@ class GreensFunction1DAbsAbs: public GreensFunction // The minimum static const int MIN_TERMS = 20; -public: - enum EventKind - { - IV_ESCAPE, - IV_REACTION - }; public: GreensFunction1DAbsAbs(Real D, Real r0, Real sigma, Real a) @@ -204,4 +198,4 @@ class GreensFunction1DAbsAbs: public GreensFunction // This is the time scale of the system, used by drawTime_f Real t_scale; }; -#endif // __FIRSTPASSAGEGREENSFUNCTION1D_HPP +#endif // __GREENSFUNCTION1DABSABS_HPP diff --git a/GreensFunction1DRadAbs.hpp b/GreensFunction1DRadAbs.hpp index 07032333..2e1836b6 100644 --- a/GreensFunction1DRadAbs.hpp +++ b/GreensFunction1DRadAbs.hpp @@ -1,5 +1,5 @@ -#if !defined( __FIRSTPASSAGEGREENSFUNCTION1DRAD_HPP ) -#define __FIRSTPASSAGEGREENSFUNCTION1DRAD_HPP +#if !defined( __GREENSFUNCTION1DRADABS_HPP ) +#define __GREENSFUNCTION1DRADABS_HPP #include #include @@ -48,7 +48,7 @@ class GreensFunction1DRadAbs: public GreensFunction // The constructor is overloaded and can be called with or without drift v // copy constructor including drift variable v - GreensFunction1DRadAbs(Real D, Real k, Real v, Real r0, Real sigma, Real a) + GreensFunction1DRadAbs(Real D, Real v, Real k, Real r0, Real sigma, Real a) : GreensFunction(D), v(v), k(k), r0(r0), sigma(sigma), a(a), l_scale(L_TYPICAL), t_scale(T_TYPICAL) { // do nothing @@ -234,4 +234,4 @@ class GreensFunction1DRadAbs: public GreensFunction // This is the time scale of the system. Real t_scale; }; -#endif // __FIRSTPASSAGEGREENSFUNCTION1DRAD_HPP +#endif // __GREENSFUNCTION1DRADABS_HPP diff --git a/pair.py b/pair.py index f9e2643a..5af3f211 100644 --- a/pair.py +++ b/pair.py @@ -89,11 +89,10 @@ def get_v_tot(self): return self.single2.pid_particle_pair[1].v - \ self.single1.pid_particle_pair[1].v v_tot = property(get_v_tot) - - v_r = 0 # Todo. + + v_r = property(get_v_tot) def get_v_R(self): - return 0 # Todo. return (self.single1.pid_particle_pair[1].v * self.single2.pid_particle_pair[1].D + self.single2.pid_particle_pair[1].v * From a137987e54aa19c3d23be081ae2f3f2943db4b8a Mon Sep 17 00:00:00 2001 From: Thomas Sokolowski Date: Mon, 16 May 2011 18:31:37 +0200 Subject: [PATCH 0041/1541] Added scalable resolution for cyl. surfaces in visualisation --- visualization/pipeline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/visualization/pipeline.py b/visualization/pipeline.py index c33c092f..a1999abd 100644 --- a/visualization/pipeline.py +++ b/visualization/pipeline.py @@ -330,6 +330,7 @@ def rebuild(self): cylindrical_surfaces = \ self.add_tensor_glyph(cylindrical_surface_data, 'Cylinder', name='Cylindrical Surfaces', + resolution=RESOLUTION, scale=HELIX_RADIUS_SCALE_FACTOR) rep4 = self.show(cylindrical_surfaces) From a6c2a71d7149b07a1b1775d0375f4115cef17115 Mon Sep 17 00:00:00 2001 From: Thomas Sokolowski Date: Tue, 17 May 2011 00:30:44 +0800 Subject: [PATCH 0042/1541] Fixed order of args in on of the 1D-GF constructors. ... and made some minor changes to pair.py. --- GreensFunction1DAbsAbs.hpp | 12 +++--------- GreensFunction1DRadAbs.hpp | 8 ++++---- pair.py | 5 ++--- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/GreensFunction1DAbsAbs.hpp b/GreensFunction1DAbsAbs.hpp index 0449e966..78be41d2 100644 --- a/GreensFunction1DAbsAbs.hpp +++ b/GreensFunction1DAbsAbs.hpp @@ -1,5 +1,5 @@ -#if !defined( __FIRSTPASSAGEGREENSFUNCTION1D_HPP ) -#define __FIRSTPASSAGEGREENSFUNCTION1D_HPP +#if !defined( __GREENSFUNCTION1DABSABS_HPP ) +#define __GREENSFUNCTION1DABSABS_HPP #include #include @@ -42,12 +42,6 @@ class GreensFunction1DAbsAbs: public GreensFunction // The minimum static const int MIN_TERMS = 20; -public: - enum EventKind - { - IV_ESCAPE, - IV_REACTION - }; public: GreensFunction1DAbsAbs(Real D, Real r0, Real sigma, Real a) @@ -204,4 +198,4 @@ class GreensFunction1DAbsAbs: public GreensFunction // This is the time scale of the system, used by drawTime_f Real t_scale; }; -#endif // __FIRSTPASSAGEGREENSFUNCTION1D_HPP +#endif // __GREENSFUNCTION1DABSABS_HPP diff --git a/GreensFunction1DRadAbs.hpp b/GreensFunction1DRadAbs.hpp index 07032333..2e1836b6 100644 --- a/GreensFunction1DRadAbs.hpp +++ b/GreensFunction1DRadAbs.hpp @@ -1,5 +1,5 @@ -#if !defined( __FIRSTPASSAGEGREENSFUNCTION1DRAD_HPP ) -#define __FIRSTPASSAGEGREENSFUNCTION1DRAD_HPP +#if !defined( __GREENSFUNCTION1DRADABS_HPP ) +#define __GREENSFUNCTION1DRADABS_HPP #include #include @@ -48,7 +48,7 @@ class GreensFunction1DRadAbs: public GreensFunction // The constructor is overloaded and can be called with or without drift v // copy constructor including drift variable v - GreensFunction1DRadAbs(Real D, Real k, Real v, Real r0, Real sigma, Real a) + GreensFunction1DRadAbs(Real D, Real v, Real k, Real r0, Real sigma, Real a) : GreensFunction(D), v(v), k(k), r0(r0), sigma(sigma), a(a), l_scale(L_TYPICAL), t_scale(T_TYPICAL) { // do nothing @@ -234,4 +234,4 @@ class GreensFunction1DRadAbs: public GreensFunction // This is the time scale of the system. Real t_scale; }; -#endif // __FIRSTPASSAGEGREENSFUNCTION1DRAD_HPP +#endif // __GREENSFUNCTION1DRADABS_HPP diff --git a/pair.py b/pair.py index f9e2643a..5af3f211 100644 --- a/pair.py +++ b/pair.py @@ -89,11 +89,10 @@ def get_v_tot(self): return self.single2.pid_particle_pair[1].v - \ self.single1.pid_particle_pair[1].v v_tot = property(get_v_tot) - - v_r = 0 # Todo. + + v_r = property(get_v_tot) def get_v_R(self): - return 0 # Todo. return (self.single1.pid_particle_pair[1].v * self.single2.pid_particle_pair[1].D + self.single2.pid_particle_pair[1].v * From c5bbe0da9a3ba9c3f4569d8f56a6db22830c03e9 Mon Sep 17 00:00:00 2001 From: Thomas Sokolowski Date: Tue, 17 May 2011 00:31:37 +0800 Subject: [PATCH 0043/1541] Added scalable resolution for cyl. surfaces in visualisation --- visualization/pipeline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/visualization/pipeline.py b/visualization/pipeline.py index c33c092f..a1999abd 100644 --- a/visualization/pipeline.py +++ b/visualization/pipeline.py @@ -330,6 +330,7 @@ def rebuild(self): cylindrical_surfaces = \ self.add_tensor_glyph(cylindrical_surface_data, 'Cylinder', name='Cylindrical Surfaces', + resolution=RESOLUTION, scale=HELIX_RADIUS_SCALE_FACTOR) rep4 = self.show(cylindrical_surfaces) From e81c7c7dc0c323371b917fc848955d3228255bc4 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Mon, 16 May 2011 19:02:27 +0200 Subject: [PATCH 0044/1541] Almost entirely have run.py operational. Will fix rest next few days. ~MW --- doc/interface_functions.aux | 26 ++-- doc/interface_functions.log | 230 ++++++++++++++++++++---------------- doc/interface_functions.tex | 3 +- doc/interface_functions.toc | 26 ++-- samples/rebind/run.py | 123 +++++++++++-------- 5 files changed, 229 insertions(+), 179 deletions(-) diff --git a/doc/interface_functions.aux b/doc/interface_functions.aux index 37ee8f85..95f295ef 100644 --- a/doc/interface_functions.aux +++ b/doc/interface_functions.aux @@ -9,10 +9,10 @@ \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{8}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.2}Handling objects (surfaces, cylinders, etc)}{9}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{9}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.4}Obtaining particle information}{10}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{10}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{10}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{11}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.4}Obtaining information about particles and species}{10}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{11}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{11}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{12}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.3}Get data}{12}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.4}Get position and distance data}{12}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.5}Additional functions}{13}} @@ -22,19 +22,19 @@ \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{16}} \@writefile{toc}{\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{17}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{17}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{17}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{21}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Notes on other files}{22}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{22}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{22}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{22}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{18}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{22}} +\@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Notes on other files}{23}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{23}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{23}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{23}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{23}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{23}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{23}} \@writefile{toc}{\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{23}} \@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{23}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{23}} -\@writefile{toc}{\contentsline {section}{\numberline {3}Todo}{23}} +\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{24}} +\@writefile{toc}{\contentsline {section}{\numberline {3}Todo}{24}} \@writefile{toc}{\contentsline {section}{\numberline {4}On C++ data structures}{24}} \@writefile{toc}{\contentsline {subsection}{\numberline {4.1}SpeciesInfo}{24}} -\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Particle}{24}} +\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Particle}{25}} diff --git a/doc/interface_functions.log b/doc/interface_functions.log index a8cbf220..2916efdc 100644 --- a/doc/interface_functions.log +++ b/doc/interface_functions.log @@ -1,4 +1,4 @@ -This is pdfTeXk, Version 3.141592-1.40.3 (Web2C 7.5.6) (format=pdflatex 2010.5.19) 16 MAY 2011 16:26 +This is pdfTeXk, Version 3.141592-1.40.3 (Web2C 7.5.6) (format=pdflatex 2010.5.19) 16 MAY 2011 17:18 entering extended mode %&-line parsing enabled. **interface_functions.tex @@ -217,13 +217,13 @@ Overfull \hbox (32.9967pt too wide) in paragraph at lines 222--222 f magnitude:[] [] - +[5] Overfull \hbox (32.9967pt too wide) in paragraph at lines 222--222 [] \OT1/cmtt/m/n/10 There is no distinction between an intrinsic and an over all reaction[] [] -[5] + Overfull \hbox (32.9967pt too wide) in paragraph at lines 241--241 [] \OT1/cmtt/m/n/10 reaction rate. Units: per second. (Rough order o f magnitude:[] @@ -325,13 +325,13 @@ Overfull \hbox (27.74675pt too wide) in paragraph at lines 332--332 gh order of[] [] - +[7] Overfull \hbox (27.74675pt too wide) in paragraph at lines 332--332 [] \OT1/cmtt/m/n/10 overall reaction rate (koff) for this reaction rule to a n intrinsic[] [] -[7] + Overfull \hbox (11.99689pt too wide) in paragraph at lines 332--332 [] \OT1/cmtt/m/n/10 reaction rate (kd) with the function utils.k_d(koff, kon , kD) or[] @@ -458,395 +458,419 @@ Overfull \hbox (6.74693pt too wide) in paragraph at lines 437--437 [] [9] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 505--505 +Overfull \hbox (1.49698pt too wide) in paragraph at lines 499--499 +[] \OT1/cmtt/m/n/10 Function that returns particle position. Can take mu +ltiple[] + [] + +[10] +Overfull \hbox (22.4968pt too wide) in paragraph at lines 499--499 +[] \OT1/cmtt/m/n/10 (of which pid_particle_pair[0] is _gfrd.Pa +rticleID).[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 499--499 +[] \OT1/cmtt/m/n/10 In the two latter cases, the function re +turns the[] + [] + + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 499--499 +[] \OT1/cmtt/m/n/10 first ID of the position of the first pa +rticle of[] + [] + + +Overfull \hbox (11.99689pt too wide) in paragraph at lines 524--524 [] \OT1/cmtt/m/n/10 def __init__(self, world, rng=myrandom.rng, network_rule s=None):[] [] -[10] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 505--505 + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 524--524 [] \OT1/cmtt/m/n/10 a random number generator. By default myrand om.rng is[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 505--505 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 524--524 [] \OT1/cmtt/m/n/10 used, which uses Mersenne Twister from the G SL library.[] [] -Overfull \hbox (53.99652pt too wide) in paragraph at lines 505--505 +Overfull \hbox (53.99652pt too wide) in paragraph at lines 524--524 [] \OT1/cmtt/m/n/10 you don't need to use this, for backward com patibility only.[] [] -Overfull \hbox (41.35085pt too wide) in paragraph at lines 513--514 +Overfull \hbox (41.35085pt too wide) in paragraph at lines 532--533 \OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 536--536 +Overfull \hbox (17.24684pt too wide) in paragraph at lines 555--555 [] \OT1/cmtt/m/n/10 asynchronously. This method bursts all protective do mains and[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 536--536 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 555--555 [] \OT1/cmtt/m/n/10 the time at which to synchronize the particl es. Usually[] [] - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 536--536 +[11] +Overfull \hbox (27.74675pt too wide) in paragraph at lines 555--555 [] \OT1/cmtt/m/n/10 you will want to use the current time of the simulator:[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 536--536 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 555--555 [] \OT1/cmtt/m/n/10 This method is called stop because it is usually cal led at the[] [] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 536--536 +Overfull \hbox (17.24684pt too wide) in paragraph at lines 555--555 [] \OT1/cmtt/m/n/10 end of a simulation. It is possible to call this met hod at an[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 536--536 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 555--555 [] \OT1/cmtt/m/n/10 earlier time. For example the Logger module does thi s, because[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 536--536 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 555--555 [] \OT1/cmtt/m/n/10 it needs to know the positions of the particles at e ach log[] [] -Overfull \hbox (41.35085pt too wide) in paragraph at lines 537--538 +Overfull \hbox (41.35085pt too wide) in paragraph at lines 556--557 \OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] -Overfull \hbox (41.35085pt too wide) in paragraph at lines 548--549 +Overfull \hbox (41.35085pt too wide) in paragraph at lines 567--568 \OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] -[11] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 560--560 + +Overfull \hbox (6.74693pt too wide) in paragraph at lines 579--579 [] \OT1/cmtt/m/n/10 This function resets the "records" of the simulator. Thi s means[] [] -Overfull \hbox (1.49698pt too wide) in paragraph at lines 560--560 +Overfull \hbox (1.49698pt too wide) in paragraph at lines 579--579 [] \OT1/cmtt/m/n/10 the simulator time is reset, the step counter is reset, events[] [] -Overfull \hbox (41.35085pt too wide) in paragraph at lines 561--562 +Overfull \hbox (41.35085pt too wide) in paragraph at lines 580--581 \OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) [] - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 605--605 +[12] +Overfull \hbox (1.49698pt too wide) in paragraph at lines 614--614 []\OT1/cmtt/m/n/10 def get_closest_surface_within_radius(world, pos, radius, ig nore):[] [] -[12] [13] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 704--704 +[13] +Overfull \hbox (32.9967pt too wide) in paragraph at lines 713--713 [] \OT1/cmtt/m/n/10 """ Return a generator (using "yield") to loop over (pid , particle).[] [] - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 719--719 +[14] +Overfull \hbox (17.24684pt too wide) in paragraph at lines 728--728 [] \OT1/cmtt/m/n/10 a Species or the name of a Species. If none is s pecified,[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 719--719 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 728--728 [] \OT1/cmtt/m/n/10 all (particle identifier, particle)-pairs will b e returned.[] [] -[14] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 734--734 + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 743--743 [] \OT1/cmtt/m/n/10 a Species or the name of a Species. If none is s pecified,[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 734--734 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 743--743 [] \OT1/cmtt/m/n/10 all (particle identifier, particle)-pairs will b e returned.[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 763--763 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 772--772 [] \OT1/cmtt/m/n/10 (Species name, number of particles)-pairs will b e returned.[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 779--779 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 788--788 [] \OT1/cmtt/m/n/10 """Return a string containing the number of particles of a certain[] [] - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 779--779 +[15] +Overfull \hbox (22.4968pt too wide) in paragraph at lines 788--788 [] \OT1/cmtt/m/n/10 a string of (Species name, number of particles)- pairs will[] [] -[15] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 791--791 + +Overfull \hbox (27.74675pt too wide) in paragraph at lines 800--800 [] \OT1/cmtt/m/n/10 """Return an iterator over the protective domains in the simulator.[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 820--820 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 829--829 [] \OT1/cmtt/m/n/10 """Return three lists with all the reaction rules define d in the[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 820--820 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 829--829 [] \OT1/cmtt/m/n/10 - reaction rules between two reactants with a reacti on rate[] [] - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 828--828 +[16] +Overfull \hbox (17.24684pt too wide) in paragraph at lines 837--837 [] \OT1/cmtt/m/n/10 ReactionRule.__str__ would be good, but we are actually getting a[] [] -Overfull \hbox (1.49698pt too wide) in paragraph at lines 840--840 +Overfull \hbox (1.49698pt too wide) in paragraph at lines 849--849 [] \OT1/cmtt/m/n/10 """Return a formatted string containing all the reaction rules[] [] -[16] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 879--879 + +Overfull \hbox (17.24684pt too wide) in paragraph at lines 888--888 [] \OT1/cmtt/m/n/10 """Return True if a and b are equal, subject to given to lerances.[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 879--879 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 888--888 [] \OT1/cmtt/m/n/10 relative tolerance multipied by this typical value, and will be[] [] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 879--879 +Overfull \hbox (27.74675pt too wide) in paragraph at lines 888--888 [] \OT1/cmtt/m/n/10 """Return True if a is greater than b, subject to given tolerances.[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 879--879 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 888--888 [] \OT1/cmtt/m/n/10 """Return True if a is less than b, subject to given tol erances.[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 879--879 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 888--888 [] \OT1/cmtt/m/n/10 """Return True if a is greater or equal than b, subject to given[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 879--879 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 888--888 [] \OT1/cmtt/m/n/10 """Return True if a is less than or equal than b, subjec t to given[] [] - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 919--919 +[17] +Overfull \hbox (6.74693pt too wide) in paragraph at lines 928--928 [] \OT1/cmtt/m/n/10 """Convert a reaction rate from units 'per molar per sec ond' to[] [] -[17] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 919--919 + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 928--928 [] \OT1/cmtt/m/n/10 """Convert a reaction rate from units 'per micromolar pe r second' to[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 919--919 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 928--928 [] \OT1/cmtt/m/n/10 """Convert a concentration from units 'micromolar' to un its 'per[] [] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (17.24684pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 """Calculate the 'pseudo-'reaction rate (kD) caused by d iffusion.[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 kD is equal to 1 divided by the time it takes for two pa rticles to[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 meet each other by diffusion. It is needed when converti ng from[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 an intrinsic reaction rate to an overall reaction rates or vice[] [] - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 +[18] +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 the diffusion constant of particle A plus the di ffusion[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 the radius of particle A plus the radius of part icle B.[] [] -[18] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (kon) for a binding/ annihilation[] [] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 the overall reaction rate for the reaction rule. Units:[] [] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind ing reaction[] [] - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1074--1074 +[19] +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 This one is a bit tricky. We consider reaction rules wit h only 1[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 reactant. In case there is only 1 product also, no conve rsion in[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 necessary. But when the reaction rule has 2 products, we need to[] [] -[19] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 1074--1074 + +Overfull \hbox (22.4968pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 the overall reaction rate for the unbinding reac tion rule.[] [] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 the overall reaction rate for the reverse reacti on rule.[] [] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind ing reaction[] [] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (17.24684pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 Similar to the function k_d(), but expects an intrinsic rate (ka)[] [] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (22.4968pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 instead of an overall rate (kon) for the reversed reacti on rule as[] [] - -Overfull \hbox (38.24666pt too wide) in paragraph at lines 1074--1074 +[20] +Overfull \hbox (38.24666pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (ka) for a binding /annihilation[] [] -[20] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 + +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (kd) for an unbind ing reaction[] [] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (kd) for an unbind ing reaction[] [] -Overfull \hbox (1.49698pt too wide) in paragraph at lines 1074--1074 +Overfull \hbox (1.49698pt too wide) in paragraph at lines 1083--1083 [] \OT1/cmtt/m/n/10 Similar to the function k_off(), but expects an intrinsi c rate[] [] [21] LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <10> not available -(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 1134. +(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 1143. - [22] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1177--1177 + [22] [23] +Overfull \hbox (11.99689pt too wide) in paragraph at lines 1186--1186 [] \OT1/cmtt/m/n/10 def __init__(self, logname='log', directory='data', comm ent=''):[] [] -[23] -Overfull \hbox (374.24373pt too wide) in paragraph at lines 1216--1216 + +Overfull \hbox (374.24373pt too wide) in paragraph at lines 1225--1225 [] \OT1/cmtt/m/n/10 SpeciesInfo (identifier_type const &id, D_type const &D=0. , length_type const &r=0., structure_id_type const &s="", v_type const &v=0.)[] [] -Overfull \hbox (64.49643pt too wide) in paragraph at lines 1216--1216 +Overfull \hbox (64.49643pt too wide) in paragraph at lines 1225--1225 []\OT1/cmtt/m/n/10 template[] [] -[24] (./interface_functions.aux) ) +[24] [25] (./interface_functions.aux) ) Here is how much of TeX's memory you used: 508 strings out of 95086 7761 string characters out of 1183255 @@ -855,18 +879,18 @@ Here is how much of TeX's memory you used: 8578 words of font info for 31 fonts, out of 1200000 for 2000 28 hyphenation exceptions out of 8191 23i,8n,20p,323b,243s stack positions out of 5000i,500n,6000p,200000b,5000s - -Output written on interface_functions.pdf (24 pages, 109202 bytes). +< +/usr/share/texmf-texlive/fonts/type1/bluesky/cm/cmr7.pfb> +Output written on interface_functions.pdf (25 pages, 110108 bytes). PDF statistics: - 123 PDF objects out of 1000 (max. 8388607) + 127 PDF objects out of 1000 (max. 8388607) 0 named destinations out of 1000 (max. 131072) 1 words of extra memory for PDF output out of 10000 (max. 10000000) diff --git a/doc/interface_functions.tex b/doc/interface_functions.tex index 5a61b356..c0d8983d 100644 --- a/doc/interface_functions.tex +++ b/doc/interface_functions.tex @@ -438,7 +438,6 @@ \subsubsection{Adding particles} \subsubsection{Obtaining information about particles and species} -\begin{verbatim} \begin{verbatim} def get_species(self): @@ -1228,4 +1227,4 @@ \subsection{Particle} The particle information is held by a class in the C++ code. A single particle is -\end{document} +\end{document} \ No newline at end of file diff --git a/doc/interface_functions.toc b/doc/interface_functions.toc index 9ac91ea3..aaa7b0df 100644 --- a/doc/interface_functions.toc +++ b/doc/interface_functions.toc @@ -8,10 +8,10 @@ \contentsline {subsubsection}{\numberline {2.2.1}Core functions}{8} \contentsline {subsubsection}{\numberline {2.2.2}Handling objects (surfaces, cylinders, etc)}{9} \contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{9} -\contentsline {subsubsection}{\numberline {2.2.4}Obtaining particle information}{10} -\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{10} -\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{10} -\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{11} +\contentsline {subsubsection}{\numberline {2.2.4}Obtaining information about particles and species}{10} +\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{11} +\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{11} +\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{12} \contentsline {subsubsection}{\numberline {2.3.3}Get data}{12} \contentsline {subsubsection}{\numberline {2.3.4}Get position and distance data}{12} \contentsline {subsubsection}{\numberline {2.3.5}Additional functions}{13} @@ -21,19 +21,19 @@ \contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{16} \contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{17} \contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{17} -\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{17} -\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{21} -\contentsline {subsection}{\numberline {2.6}Notes on other files}{22} -\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{22} -\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{22} -\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{22} +\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{18} +\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{22} +\contentsline {subsection}{\numberline {2.6}Notes on other files}{23} +\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{23} +\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{23} +\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{23} \contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{23} \contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{23} \contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{23} \contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{23} \contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{23} -\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{23} -\contentsline {section}{\numberline {3}Todo}{23} +\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{24} +\contentsline {section}{\numberline {3}Todo}{24} \contentsline {section}{\numberline {4}On C++ data structures}{24} \contentsline {subsection}{\numberline {4.1}SpeciesInfo}{24} -\contentsline {subsection}{\numberline {4.2}Particle}{24} +\contentsline {subsection}{\numberline {4.2}Particle}{25} diff --git a/samples/rebind/run.py b/samples/rebind/run.py index 057ed525..31d37fd9 100644 --- a/samples/rebind/run.py +++ b/samples/rebind/run.py @@ -4,9 +4,9 @@ """ See README for a description -# D_factor N_B N_X N +# [N_B] [N_X] [N] -LOGLEVEL=ERROR PYTHONPATH=../.. python -O run.py 1 1 100 10 +LOGLEVEL=ERROR PYTHONPATH=../.. python -O run.py 1 100 10 """ @@ -17,16 +17,23 @@ import gfrdbase import model -def run(outfilename, D_factor, N_B, N_X, N): +def run(outfilename, N_B, N_X, T_list, N): """ Function that loops over single runs of binding/rebinding simulations. (Plus some overhead code.) Arguments: - - + - outfilename: This string can be found back in the + output filename. + - T_list: List of times at which distance between + particles is evaluated and logged. + - N_B: Number of instances ("molecules") of + particle B. + - N_X: Number of instances ("molecules") of + particle X. - """ #TODO - global DX_factor, V, L, matrix_size + """ + global DX_factor, V, L, matrix_size, D, radius, DX, kf, tau print "Printing output to: " + outfilename @@ -50,12 +57,15 @@ def run(outfilename, D_factor, N_B, N_X, N): matrix_size = min(max(3, int((9 * (N_X+N_B)) ** (1.0/3.0))), 60) print 'matrix_size=', matrix_size - # This is the list with steps - T_list = [tau * .1, INF] + # Overrides the T_list input + T_list = [tau*0.1, tau, tau*10, tau * 100, tau * 1000, INF] + T_list = range(0, int(tau*100000), 100) + T_list.append(INF) # Clear the distance output file - outfile_r = open(outfilename + '_r_-'+j+'.dat', 'a') - outfile_r.close() + for j in T_list: + outfile_r = open(outfilename + '_r_-'+str(j)+'.dat', 'a') + outfile_r.close() # Open the time output file outfile_t = open(outfilename + '_t.dat', 'w') @@ -64,7 +74,8 @@ def run(outfilename, D_factor, N_B, N_X, N): for i in range(N): # Perform a single simulation run - r_list, t_list = singlerun(T_list, D_factor, N_B, N_X) + print "###### Run " + str(i) + "." + r_list, t_list = singlerun(T_list, N_B, N_X) # Write away time data for t in t_list: @@ -75,12 +86,13 @@ def run(outfilename, D_factor, N_B, N_X, N): # with a list of distances corresponding to measure- # ments at times from T_list.) for j in range(len(r_list)): - outfile_r = open(outfilename + '_r_-'+j+'.dat', 'a') - outfile_r.write('%g\n' % r_list[j]) + outfile_r = open(outfilename + '_r_-' + str(T_list[j]) +'.dat', 'a') + outfile_r.write(str(r_list[j]) + '\n') outfile_r.close() # Print to screen. - print i, r_list, t_list + print "* Output: t_list=" + str(t_list) + print "* Output: r_list=" + str(r_list) # Make sure script writes data NOW outfile_t.flush() @@ -88,15 +100,19 @@ def run(outfilename, D_factor, N_B, N_X, N): # Close time output file outfile_t.close() -def singlerun(T_list, D_factor, N_B, N_X): +def singlerun(T_list, N_B, N_X): """ Function that performs one simulation run of binding/ rebinding. Arguments: - - - - """ #TODO + - T_list: List of times at which distance between + particles is evaluated and logged. + - N_B: Number of instances ("molecules") of + particle B. + - N_X: Number of instances ("molecules") of + particle X. + """ ################# Define the model: @@ -159,20 +175,21 @@ def singlerun(T_list, D_factor, N_B, N_X): leads to infinite loop) - + """#TODO + """This code doesn't function properly, because you want to clear + particles within a certain radius.. Now you can be removing particles + that after bursting are not in the area anymore, but whose bursted + domains were. + + After bursting you should just check whether something is within the radius. + Perhaps it would be more easy to just place all the particles at the beginning, but give A and B initially a diffusion constant of 0, then stirr, and then set the diffusion constant to their - desired values.. - - """ - print "Started clearing.." + desired values..""" + while 1: - #TODO this code doesn't function properly, because you want to clear - # particles within a certain radius.. Now you can be removing particles - # that after bursting are not in the area anymore, but whose bursted - # domains were. - # After bursting you should just check whether something is within the radius. #pp = s.get_particles_within_radius(A_pos, float(A['radius'])) dd = s.clear_volume(A_pos, float(A['radius'])) # returns a list of @@ -184,7 +201,7 @@ def singlerun(T_list, D_factor, N_B, N_X): for d in dd: for p in d.particle_id_pair: s.remove_particle(p) - print "Removing particle.." + print "* Removing particle to make space for A.." s.throw_in_particles(X, len(pp), box1) place_particle(w, A, A_pos) @@ -200,23 +217,23 @@ def singlerun(T_list, D_factor, N_B, N_X): for d in dd: for p in d.particle_id_pair: s.remove_particle(p) - print "Removing particle.." + print "* Removing particle to make space for B.." s.throw_in_particles(X, len(pp), box1) place_particle(w, B, B_pos) - print "Finished placing particles A & B.." # Place rest of B at random positions if N_B > 1: - s.throw_in_particles(B, N_B-1, box1) + gfrdbase.throw_in_particles(w, B, N_B-1) + # Create some vars and perform first simulation step r_list = [] t_list = [] t_last = 0 s.step() - next_stop = T_list[0] # In this case T_list[0] equals infinity + next_stop = T_list[0] i_T = 0 @@ -235,42 +252,50 @@ def singlerun(T_list, D_factor, N_B, N_X): current time (s.t) and last reaction time (t_last). """ if s.last_reaction: # aka if there was a reaction - print s.last_reaction + print "* Reaction detected at: " + str(s.last_reaction) if len(s.world.get_particle_ids(C.id)) == 0: #A,B - print 'set t_last', s.t + print ' - set t_last', str(s.t) t_last = s.t # set t_last else: # C - print 'reaction: ', s.t - t_last + print ' - reaction: ', str(s.t - t_last) t_list.append(s.t - t_last) """ If it's time to log, then log distance between particles. """ next_time = s.get_next_time() if next_time > next_stop: - print 'stop', i_T, next_stop + print '* Measuring distances at ', str(i_T), str(next_stop) s.stop(next_stop) + print '* stopped' + # If there is a particle C, the distance is "0". if len(s.world.get_particle_ids(C.id)) != 0: #A,B r_list.append(0) # If particles are manifested as A and B, log distance # inbetween. else: # C = 0, i.e. there are particles A and B - # r_list.append(s.distance_between_particles(A.id, B.id)) - # If there's only 1 B-particle - particlesA = w.get_particle_ids(A.id) - particlesB = w.get_particle_ids(B.id) - positionA = particlesA[1].position() - positionB = particlesB[1].position() - particle = self.sim.world.get_particle(particle_id)[1] - - r_list.append(w.distance(positionA, positionB)) + # Calculate distances: + A_pos = s.get_position(A.id) + distance_list = [] + # If there are >1 B particles, we want to log all + # distances seperately, hence the loop: + for particle_id in w.get_particle_ids(B.id): + B_pos = s.get_position(particle_id) + distance = w.distance(A_pos, B_pos) + distance_list.append(distance) + print "* Measured distance = " + str(distance) + # Write list to distances table + r_list.append(distance_list) i_T += 1 next_stop = T_list[i_T] + print "* Done, returning to simulation loop." """ If there are no measuring moments on the list any more, stop """ - if next_stop == INF and len(t_list) != 0: - print 'break', s.t + if (next_stop == INF): + print '* Break at ', str(s.t) + if (len(t_list) == 0): + t_list.append(float('nan')) break s.step() @@ -282,10 +307,12 @@ def singlerun(T_list, D_factor, N_B, N_X): import os + T_list = eval(sys.argv[3]) + outfilename = 'data/rebind_' + '_'.join(sys.argv[1:4]) +\ '_' #+ os.environ['SGE_TASK_ID'] run(outfilename, float(sys.argv[1]), - int(sys.argv[2]), int(sys.argv[3]), int(sys.argv[4])) + int(sys.argv[2]), T_list, int(sys.argv[4])) From 3667d4f0d72c05232c3bc1b5cdb76fc4fbe36c76 Mon Sep 17 00:00:00 2001 From: Joris Paijmans Date: Tue, 17 May 2011 12:16:34 +0200 Subject: [PATCH 0045/1541] Added get_v and set_v to the Python bindings for the Particle object So now something like particle1.pid_particle_pair[1].v can be called. --- binding/Particle.hpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/binding/Particle.hpp b/binding/Particle.hpp index d8aff859..dfc07ef7 100644 --- a/binding/Particle.hpp +++ b/binding/Particle.hpp @@ -232,6 +232,20 @@ class ParticleWrapper return 0; } + static PyObject* get_v(ParticleWrapper* self) + { + return PyFloat_FromDouble(self->impl_.v()); + } + + static int set_v(ParticleWrapper* self, PyObject* val, void *) + { + const double tmp(PyFloat_AsDouble(val)); + if (PyErr_Occurred()) + return -1; + self->impl_.v() = tmp; + return 0; + } + static PyObject* get_sid(ParticleWrapper* self) { return boost::python::incref( @@ -292,7 +306,7 @@ class ParticleWrapper static PyObject* __sq_item__(PyObject *self, Py_ssize_t idx) { - if (idx < 0 || idx >= 4) + if (idx < 0 || idx >= 5) { PyErr_SetString(PyExc_IndexError, "index out of range"); return NULL; @@ -306,7 +320,9 @@ class ParticleWrapper return get_radius(reinterpret_cast(self)); case 2: return get_D(reinterpret_cast(self)); - case 3: + case 3: + return get_v(reinterpret_cast(self)); + case 4: return get_sid(reinterpret_cast(self)); } return NULL; // never get here @@ -314,7 +330,7 @@ class ParticleWrapper static int __sq_ass_item__(PyObject *self, Py_ssize_t idx, PyObject *val) { - if (idx < 0 || idx >= 4) + if (idx < 0 || idx >= 5) { PyErr_SetString(PyExc_IndexError, "index out of range"); return -1; @@ -328,7 +344,9 @@ class ParticleWrapper return set_radius(reinterpret_cast(self), val, 0); case 2: return set_D(reinterpret_cast(self), val, 0); - case 3: + case 3: + return set_v(reinterpret_cast(self), val, 0); + case 4: return set_sid(reinterpret_cast(self), val, 0); } @@ -429,6 +447,12 @@ PyGetSetDef ParticleWrapper::__getsets__[] = { (setter)ParticleWrapper::set_D, const_cast("") }, + { + const_cast("v"), + (getter)ParticleWrapper::get_v, + (setter)ParticleWrapper::set_v, + const_cast("") + }, { const_cast("sid"), (getter)ParticleWrapper::get_sid, From 58c287b93274937a14564803c9bc1e40748c5f67 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Tue, 17 May 2011 12:17:48 +0200 Subject: [PATCH 0046/1541] Fixed cosmetics: docstrings/comment style and trashed redundant files. Thomas Meidema (Thomie) pointed out that functions that are not intended for public use shouldn't have docstrings. Since comments are helpfull though, functions not intended for public use can have comments in blockquotes. Tried to fix above where I messed it up. ~MW --- doc/interface_functions.aux | 40 -- doc/interface_functions.log | 896 ------------------------------------ doc/interface_functions.tex | 51 +- doc/interface_functions.toc | 39 -- dumper.py | 80 ++-- gfrdbase.py | 2 +- 6 files changed, 72 insertions(+), 1036 deletions(-) delete mode 100644 doc/interface_functions.aux delete mode 100644 doc/interface_functions.log delete mode 100644 doc/interface_functions.toc diff --git a/doc/interface_functions.aux b/doc/interface_functions.aux deleted file mode 100644 index 95f295ef..00000000 --- a/doc/interface_functions.aux +++ /dev/null @@ -1,40 +0,0 @@ -\relax -\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{2}} -\@writefile{toc}{\contentsline {section}{\numberline {2}Functions}{2}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.1}Functions from \texttt {model.py}}{2}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.1}Core functions:}{2}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.2}Creating regions}{3}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{5}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{8}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{8}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.2}Handling objects (surfaces, cylinders, etc)}{9}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{9}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.2.4}Obtaining information about particles and species}{10}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{11}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{11}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{12}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.3}Get data}{12}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.4}Get position and distance data}{12}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.3.5}Additional functions}{13}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{13}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{13}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{16}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{16}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{17}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{17}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{18}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{22}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.6}Notes on other files}{23}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{23}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{23}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{23}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{23}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{23}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{23}} -\@writefile{toc}{\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{23}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{23}} -\@writefile{toc}{\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{24}} -\@writefile{toc}{\contentsline {section}{\numberline {3}Todo}{24}} -\@writefile{toc}{\contentsline {section}{\numberline {4}On C++ data structures}{24}} -\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}SpeciesInfo}{24}} -\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Particle}{25}} diff --git a/doc/interface_functions.log b/doc/interface_functions.log deleted file mode 100644 index 2916efdc..00000000 --- a/doc/interface_functions.log +++ /dev/null @@ -1,896 +0,0 @@ -This is pdfTeXk, Version 3.141592-1.40.3 (Web2C 7.5.6) (format=pdflatex 2010.5.19) 16 MAY 2011 17:18 -entering extended mode - %&-line parsing enabled. -**interface_functions.tex -(./interface_functions.tex -LaTeX2e <2005/12/01> -Babel and hyphenation patterns for english, usenglishmax, dumylang, noh -yphenation, loaded. -(/usr/share/texmf-texlive/tex/latex/base/article.cls -Document Class: article 2005/09/16 v1.4f Standard LaTeX document class -(/usr/share/texmf-texlive/tex/latex/base/size10.clo -File: size10.clo 2005/09/16 v1.4f Standard LaTeX file (size option) -) -\c@part=\count79 -\c@section=\count80 -\c@subsection=\count81 -\c@subsubsection=\count82 -\c@paragraph=\count83 -\c@subparagraph=\count84 -\c@figure=\count85 -\c@table=\count86 -\abovecaptionskip=\skip41 -\belowcaptionskip=\skip42 -\bibindent=\dimen102 -) -(/usr/share/texmf-texlive/tex/latex/base/inputenc.sty -Package: inputenc 2006/05/05 v1.1b Input encoding file -\inpenc@prehook=\toks14 -\inpenc@posthook=\toks15 - -(/usr/share/texmf-texlive/tex/latex/ucs/utf8x.def -File: utf8x.def 2004/10/17 UCS: Input encoding UTF-8 -)) -(/usr/share/texmf-texlive/tex/latex/ucs/ucs.sty -Package: ucs 2004/10/17 UCS: Unicode input support - -(/usr/share/texmf-texlive/tex/latex/ucs/data/uni-global.def -File: uni-global.def 2004/10/17 UCS: Unicode global data -) -\uc@secondtry=\count87 -\uc@combtoks=\toks16 -\uc@combtoksb=\toks17 -\uc@temptokena=\toks18 -) -(./interface_functions.aux) -\openout1 = `interface_functions.aux'. - -LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 8. -LaTeX Font Info: ... okay on input line 8. -LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 8. -LaTeX Font Info: ... okay on input line 8. -LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 8. -LaTeX Font Info: ... okay on input line 8. -LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 8. -LaTeX Font Info: ... okay on input line 8. -LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 8. -LaTeX Font Info: ... okay on input line 8. -LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 8. -LaTeX Font Info: ... okay on input line 8. - (/usr/share/texmf-texlive/tex/latex/ucs/ucsencs.def -File: ucsencs.def 2003/11/29 Fixes to fontencodings LGR, T3 -) -LaTeX Font Info: External font `cmex10' loaded for size -(Font) <12> on input line 10. -LaTeX Font Info: External font `cmex10' loaded for size -(Font) <8> on input line 10. -LaTeX Font Info: External font `cmex10' loaded for size -(Font) <6> on input line 10. - (./interface_functions.toc -LaTeX Font Info: External font `cmex10' loaded for size -(Font) <7> on input line 3. -LaTeX Font Info: External font `cmex10' loaded for size -(Font) <5> on input line 3. - [1 - -{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}]) -\tf@toc=\write3 -\openout3 = `interface_functions.toc'. - -LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <12> not available -(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 21. - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 46--46 -[] \OT1/cmtt/m/n/10 the size of one side of the simulation "worl -d". Units:[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 46--46 -[] \OT1/cmtt/m/n/10 The simulation "world" is always assumed to be a cub -e with[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 46--46 -[] \OT1/cmtt/m/n/10 *periodic boundary conditions*, with 1 corner at [0, - 0, 0] and[] - [] - -[2] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 76--76 -[] \OT1/cmtt/m/n/10 the radius for this Species in/on this Region or - Surface.[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 76--76 -[] \OT1/cmtt/m/n/10 the Region or Surface in/on which this Species c -an exist.[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 76--76 -[] \OT1/cmtt/m/n/10 Optional. If you do not specify a Structure the -Species is[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 76--76 -[] \OT1/cmtt/m/n/10 If a certain Species should be able to exist in the "wor -ld" as[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 76--76 -[] \OT1/cmtt/m/n/10 well as in/on one of the previously created Regions or S -urfaces,[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 164--164 -[] \OT1/cmtt/m/n/10 the vector [x, y, z] from the corner closest to [0, -0, 0], to[] - [] - -[3] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 164--164 -[]\OT1/cmtt/m/n/10 """create_cylindrical_surface(id, corner, radius, orientatio -n, length)[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 164--164 -[] \OT1/cmtt/m/n/10 the point [x, y, z] on the axis of the cylinder clos -est to[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 164--164 -[] \OT1/cmtt/m/n/10 the unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] al -ong the[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 164--164 -[] \OT1/cmtt/m/n/10 the length of the cylinder. Should be equal to the w -orld_size.[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 164--164 -[]\OT1/cmtt/m/n/10 """create_planar_surface(id, corner, unit_x, unit_y, length_ -x, length_y)[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 164--164 -[] \OT1/cmtt/m/n/10 the point [x, y, z] on the plane closest to [0, 0, 0 -]. Units:[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 164--164 -[] \OT1/cmtt/m/n/10 a unit vector [1, 0, 0], [0, 1, 0] or [0, 0, 1] alon -g the plane[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 164--164 -[] \OT1/cmtt/m/n/10 the length of the plane along the unit vector unit_x -. Should be[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 164--164 -[] \OT1/cmtt/m/n/10 the length of the plane along the unit vector unit_y -. Should be[] - [] - -[4] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 180--180 -[] \OT1/cmtt/m/n/10 """Add a Structure (Region or Surface) to the Partic -leModel.[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 180--180 -[] \OT1/cmtt/m/n/10 a Region or Surface created with one of the fu -nctions[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 198--198 -[] \OT1/cmtt/m/n/10 """Set all 'other' possible ReactionRules to be repu -lsive.[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 198--198 -[] \OT1/cmtt/m/n/10 - a repulsive bimolecular reaction rule (k=0) fo -r each[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 222--222 -[] \OT1/cmtt/m/n/10 reaction rate. Units: per second. (Rough order o -f magnitude:[] - [] - -[5] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 222--222 -[] \OT1/cmtt/m/n/10 There is no distinction between an intrinsic and an over -all reaction[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 241--241 -[] \OT1/cmtt/m/n/10 reaction rate. Units: per second. (Rough order o -f magnitude:[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 241--241 -[] \OT1/cmtt/m/n/10 There is no distinction between an intrinsic and an over -all reaction[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 269--269 -[] \OT1/cmtt/m/n/10 intrinsic reaction rate. Units: meters^3 per sec -ond. (Rough[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 269--269 -[] \OT1/cmtt/m/n/10 overall reaction rate (kon) to an intrinsic reaction rat -e (ka) with[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 269--269 -[] \OT1/cmtt/m/n/10 the function utils.k_a(kon, kD), but only for reaction r -ules in 3D.[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 269--269 -[] \OT1/cmtt/m/n/10 bimolecular reaction rule (ka=0) for each possible combi -nation of[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 269--269 -[] \OT1/cmtt/m/n/10 reactants for which no bimolecular reaction rule is spec -ified.[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 269--269 -[] \OT1/cmtt/m/n/10 You can explicitly add these reaction rules to the model - with the[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 302--302 -[]\OT1/cmtt/m/n/10 def create_binding_reaction_rule(reactant1, reactant2, produ -ct, ka):[] - [] - -[6] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 302--302 -[] \OT1/cmtt/m/n/10 intrinsic reaction rate. Units: meters^3 per sec -ond. (Rough[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 302--302 -[] \OT1/cmtt/m/n/10 overall reaction rate (kon) to an intrinsic reaction rat -e (ka) with[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 302--302 -[] \OT1/cmtt/m/n/10 the function utils.k_a(kon, kD), but only for reaction r -ules in 3D.[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 302--302 -[] \OT1/cmtt/m/n/10 bimolecular reaction rule (ka=0) for each possible combi -nation of[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 302--302 -[] \OT1/cmtt/m/n/10 reactants for which no bimolecular reaction rule is spec -ified.[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 302--302 -[] \OT1/cmtt/m/n/10 You can explicitly add these reaction rules to the model - with the[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 332--332 -[]\OT1/cmtt/m/n/10 def create_unbinding_reaction_rule(reactant, product1, produ -ct2, kd):[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 332--332 -[] \OT1/cmtt/m/n/10 intrinsic reaction rate. Units: per second. (Rou -gh order of[] - [] - -[7] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 332--332 -[] \OT1/cmtt/m/n/10 overall reaction rate (koff) for this reaction rule to a -n intrinsic[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 332--332 -[] \OT1/cmtt/m/n/10 reaction rate (kd) with the function utils.k_d(koff, kon -, kD) or[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 The world object keeps track of the positions of the par -ticles[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 a ParticleModel previously created with model.Pa -rticleModel.[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 the number of cells in the MatrixSpace along the - x, y and z[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 axis. Leave it to the default number if you don' -t know what[] - [] - - -Overfull \hbox (43.49661pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 The simulation cube "world" is divided into (matrix_size - x matrix_size[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 x matrix_size) cells. Together these cells form a Matrix -Space. The[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 MatrixSpace keeps track in which cell every particle and - protective[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 neighbours of particle, only objects in the same cell an -d the 26[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 (3x3x3 - 1) neighbouring cells (the simulation cube has -periodic[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 The matrix_size limits the size of the protective domain -s. If you[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 have fewer particles, you want a smaller matrix_size, su -ch that the[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 protective domains and thus the eGFRD timesteps can be l -arger. If[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 you have more particles, you want a larger matrix_size, -such that[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 378--378 -[] \OT1/cmtt/m/n/10 (N * 6) ** (1. / 3.) is used, where N is the average num -ber of[] - [] - -[8] -Overfull \hbox (1.49698pt too wide) in paragraph at lines 400--400 -[]\OT1/cmtt/m/n/10 def get_closest_surface_within_radius(world, pos, radius, ig -nore):[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 419--419 -[] \OT1/cmtt/m/n/10 """Add n particles of a certain Species to the specified - world.[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 419--419 -[] \OT1/cmtt/m/n/10 Make sure to first add the Species to the model with the - method[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 437--437 -[] \OT1/cmtt/m/n/10 """Place a particle of a certain Species at a specific p -osition in[] - [] - - -Overfull \hbox (38.24666pt too wide) in paragraph at lines 437--437 -[] \OT1/cmtt/m/n/10 a position vector [x, y, z]. Units: [meters, met -ers, meters].[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 437--437 -[] \OT1/cmtt/m/n/10 Make sure to first add the Species to the model with the - method[] - [] - -[9] -Overfull \hbox (1.49698pt too wide) in paragraph at lines 499--499 -[] \OT1/cmtt/m/n/10 Function that returns particle position. Can take mu -ltiple[] - [] - -[10] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 499--499 -[] \OT1/cmtt/m/n/10 (of which pid_particle_pair[0] is _gfrd.Pa -rticleID).[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 499--499 -[] \OT1/cmtt/m/n/10 In the two latter cases, the function re -turns the[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 499--499 -[] \OT1/cmtt/m/n/10 first ID of the position of the first pa -rticle of[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 524--524 -[] \OT1/cmtt/m/n/10 def __init__(self, world, rng=myrandom.rng, network_rule -s=None):[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 524--524 -[] \OT1/cmtt/m/n/10 a random number generator. By default myrand -om.rng is[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 524--524 -[] \OT1/cmtt/m/n/10 used, which uses Mersenne Twister from the G -SL library.[] - [] - - -Overfull \hbox (53.99652pt too wide) in paragraph at lines 524--524 -[] \OT1/cmtt/m/n/10 you don't need to use this, for backward com -patibility only.[] - [] - - -Overfull \hbox (41.35085pt too wide) in paragraph at lines 532--533 -\OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ -cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 555--555 -[] \OT1/cmtt/m/n/10 asynchronously. This method bursts all protective do -mains and[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 555--555 -[] \OT1/cmtt/m/n/10 the time at which to synchronize the particl -es. Usually[] - [] - -[11] -Overfull \hbox (27.74675pt too wide) in paragraph at lines 555--555 -[] \OT1/cmtt/m/n/10 you will want to use the current time of the - simulator:[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 555--555 -[] \OT1/cmtt/m/n/10 This method is called stop because it is usually cal -led at the[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 555--555 -[] \OT1/cmtt/m/n/10 end of a simulation. It is possible to call this met -hod at an[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 555--555 -[] \OT1/cmtt/m/n/10 earlier time. For example the Logger module does thi -s, because[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 555--555 -[] \OT1/cmtt/m/n/10 it needs to know the positions of the particles at e -ach log[] - [] - - -Overfull \hbox (41.35085pt too wide) in paragraph at lines 556--557 -\OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ -cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) - [] - - -Overfull \hbox (41.35085pt too wide) in paragraph at lines 567--568 -\OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ -cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 579--579 -[] \OT1/cmtt/m/n/10 This function resets the "records" of the simulator. Thi -s means[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 579--579 -[] \OT1/cmtt/m/n/10 the simulator time is reset, the step counter is reset, -events[] - [] - - -Overfull \hbox (41.35085pt too wide) in paragraph at lines 580--581 -\OT1/cmr/m/n/10 (Function be-longs to EGFRD-Sim-u-la-tor class, call with \OT1/ -cmtt/m/n/10 EGFRDSimulator.get[]next[]time\OT1/cmr/m/n/10 .) - [] - -[12] -Overfull \hbox (1.49698pt too wide) in paragraph at lines 614--614 -[]\OT1/cmtt/m/n/10 def get_closest_surface_within_radius(world, pos, radius, ig -nore):[] - [] - -[13] -Overfull \hbox (32.9967pt too wide) in paragraph at lines 713--713 -[] \OT1/cmtt/m/n/10 """ Return a generator (using "yield") to loop over (pid -, particle).[] - [] - -[14] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 728--728 -[] \OT1/cmtt/m/n/10 a Species or the name of a Species. If none is s -pecified,[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 728--728 -[] \OT1/cmtt/m/n/10 all (particle identifier, particle)-pairs will b -e returned.[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 743--743 -[] \OT1/cmtt/m/n/10 a Species or the name of a Species. If none is s -pecified,[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 743--743 -[] \OT1/cmtt/m/n/10 all (particle identifier, particle)-pairs will b -e returned.[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 772--772 -[] \OT1/cmtt/m/n/10 (Species name, number of particles)-pairs will b -e returned.[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 788--788 -[] \OT1/cmtt/m/n/10 """Return a string containing the number of particles of - a certain[] - [] - -[15] -Overfull \hbox (22.4968pt too wide) in paragraph at lines 788--788 -[] \OT1/cmtt/m/n/10 a string of (Species name, number of particles)- -pairs will[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 800--800 -[] \OT1/cmtt/m/n/10 """Return an iterator over the protective domains in the - simulator.[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 829--829 -[] \OT1/cmtt/m/n/10 """Return three lists with all the reaction rules define -d in the[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 829--829 -[] \OT1/cmtt/m/n/10 - reaction rules between two reactants with a reacti -on rate[] - [] - -[16] -Overfull \hbox (17.24684pt too wide) in paragraph at lines 837--837 -[] \OT1/cmtt/m/n/10 ReactionRule.__str__ would be good, but we are actually -getting a[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 849--849 -[] \OT1/cmtt/m/n/10 """Return a formatted string containing all the reaction - rules[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 888--888 -[] \OT1/cmtt/m/n/10 """Return True if a and b are equal, subject to given to -lerances.[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 888--888 -[] \OT1/cmtt/m/n/10 relative tolerance multipied by this typical value, and -will be[] - [] - - -Overfull \hbox (27.74675pt too wide) in paragraph at lines 888--888 -[] \OT1/cmtt/m/n/10 """Return True if a is greater than b, subject to given -tolerances.[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 888--888 -[] \OT1/cmtt/m/n/10 """Return True if a is less than b, subject to given tol -erances.[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 888--888 -[] \OT1/cmtt/m/n/10 """Return True if a is greater or equal than b, subject -to given[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 888--888 -[] \OT1/cmtt/m/n/10 """Return True if a is less than or equal than b, subjec -t to given[] - [] - -[17] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 928--928 -[] \OT1/cmtt/m/n/10 """Convert a reaction rate from units 'per molar per sec -ond' to[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 928--928 -[] \OT1/cmtt/m/n/10 """Convert a reaction rate from units 'per micromolar pe -r second' to[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 928--928 -[] \OT1/cmtt/m/n/10 """Convert a concentration from units 'micromolar' to un -its 'per[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 """Calculate the 'pseudo-'reaction rate (kD) caused by d -iffusion.[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 kD is equal to 1 divided by the time it takes for two pa -rticles to[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 meet each other by diffusion. It is needed when converti -ng from[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 an intrinsic reaction rate to an overall reaction rates -or vice[] - [] - -[18] -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 the diffusion constant of particle A plus the di -ffusion[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 the radius of particle A plus the radius of part -icle B.[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (kon) for a binding/ -annihilation[] - [] - - -Overfull \hbox (6.74693pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 the overall reaction rate for the reaction rule. - Units:[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind -ing reaction[] - [] - -[19] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 This one is a bit tricky. We consider reaction rules wit -h only 1[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 reactant. In case there is only 1 product also, no conve -rsion in[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 necessary. But when the reaction rule has 2 products, we - need to[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 the overall reaction rate for the unbinding reac -tion rule.[] - [] - - -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 the overall reaction rate for the reverse reacti -on rule.[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 """Convert an overall reaction rate (koff) for an unbind -ing reaction[] - [] - - -Overfull \hbox (17.24684pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 Similar to the function k_d(), but expects an intrinsic -rate (ka)[] - [] - - -Overfull \hbox (22.4968pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 instead of an overall rate (kon) for the reversed reacti -on rule as[] - [] - -[20] -Overfull \hbox (38.24666pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (ka) for a binding -/annihilation[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (kd) for an unbind -ing reaction[] - [] - - -Overfull \hbox (32.9967pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 """Convert an intrinsic reaction rate (kd) for an unbind -ing reaction[] - [] - - -Overfull \hbox (1.49698pt too wide) in paragraph at lines 1083--1083 -[] \OT1/cmtt/m/n/10 Similar to the function k_off(), but expects an intrinsi -c rate[] - [] - -[21] -LaTeX Font Info: Font shape `OT1/cmtt/bx/n' in size <10> not available -(Font) Font shape `OT1/cmtt/m/n' tried instead on input line 1143. - - [22] [23] -Overfull \hbox (11.99689pt too wide) in paragraph at lines 1186--1186 -[] \OT1/cmtt/m/n/10 def __init__(self, logname='log', directory='data', comm -ent=''):[] - [] - - -Overfull \hbox (374.24373pt too wide) in paragraph at lines 1225--1225 -[] \OT1/cmtt/m/n/10 SpeciesInfo (identifier_type const &id, D_type const &D=0. -, length_type const &r=0., structure_id_type const &s="", v_type const &v=0.)[] - - [] - - -Overfull \hbox (64.49643pt too wide) in paragraph at lines 1225--1225 -[]\OT1/cmtt/m/n/10 template[] - [] - -[24] [25] (./interface_functions.aux) ) -Here is how much of TeX's memory you used: - 508 strings out of 95086 - 7761 string characters out of 1183255 - 67134 words of memory out of 1500000 - 3731 multiletter control sequences out of 10000+50000 - 8578 words of font info for 31 fonts, out of 1200000 for 2000 - 28 hyphenation exceptions out of 8191 - 23i,8n,20p,323b,243s stack positions out of 5000i,500n,6000p,200000b,5000s -< -/usr/share/texmf-texlive/fonts/type1/bluesky/cm/cmr7.pfb> -Output written on interface_functions.pdf (25 pages, 110108 bytes). -PDF statistics: - 127 PDF objects out of 1000 (max. 8388607) - 0 named destinations out of 1000 (max. 131072) - 1 words of extra memory for PDF output out of 10000 (max. 10000000) - diff --git a/doc/interface_functions.tex b/doc/interface_functions.tex index c0d8983d..9d25c1f4 100644 --- a/doc/interface_functions.tex +++ b/doc/interface_functions.tex @@ -682,34 +682,36 @@ \subsubsection{Getting information on species/particles} \begin{verbatim} def _get_species_type_by_name(sim, name): - """ Return the type of a species with a certain name - - Arguments: - - sim - an EGFRDSimulator - - name - species name - """ + # Return the type of a species with a certain name + # (Function not intended for public use.) + # + # Arguments: + # - sim + # an EGFRDSimulator + # - name + # species name \end{verbatim} +Note that this function is NOT intended for users. It is listed because it MIGHT come in handy. (But should perhaps be removed from the list as there are other more user-friendly functions that give the same result.) \begin{verbatim} def _get_particles_by_sid(sim, sid): - """ Return a generator (using "yield") to loop over (pid, particle). - - Arguments: - - sim - an EGFRDSimulator - - sid - ID of a species - - E.g.: - - myparticles = _get_particles_by_sid(sim, sid) - - for mypid, myparticle in myparticles: - print str(str(mypid), str(myparticle)) - """ + # Return a generator (using "yield") to loop over (pid, particle). + # (Function not intended for public use.) + # + # Arguments: + # - sim + # an EGFRDSimulator + # - sid + # ID of a species + # + # E.g.: + # + # myparticles = _get_particles_by_sid(sim, sid) + # + # for mypid, myparticle in myparticles: + # print str(str(mypid), str(myparticle)) \end{verbatim} +Note that this function is NOT intended for users. It is listed because it MIGHT come in handy. (But should perhaps be removed from the list as there are other more user-friendly functions that give the same result.) \begin{verbatim} def get_particles(sim, identifier=None): @@ -725,6 +727,7 @@ \subsubsection{Getting information on species/particles} """ \end{verbatim} +There is actually a case of bad naming here. (particle identifier, particle)-pairs refers to tuples containing particle information. It has nothing to do with particles being paired in the algorithm. \begin{verbatim} def dump_particles(sim, identifier=None): @@ -740,6 +743,7 @@ \subsubsection{Getting information on species/particles} """ \end{verbatim} +There is actually a case of bad naming here. (particle identifier, particle)-pairs refers to tuples containing particle information. It has nothing to do with particles being paired in the algorithm. \begin{verbatim} def _get_number_of_particles_by_sid(sim, sid): @@ -754,6 +758,7 @@ \subsubsection{Getting information on species/particles} """ \end{verbatim} +There is actually a case of bad naming here. (particle identifier, particle)-pairs refers to tuples containing particle information. It has nothing to do with particles being paired in the algorithm. \begin{verbatim} def get_number_of_particles(sim, identifier=None): diff --git a/doc/interface_functions.toc b/doc/interface_functions.toc deleted file mode 100644 index aaa7b0df..00000000 --- a/doc/interface_functions.toc +++ /dev/null @@ -1,39 +0,0 @@ -\contentsline {section}{\numberline {1}Introduction}{2} -\contentsline {section}{\numberline {2}Functions}{2} -\contentsline {subsection}{\numberline {2.1}Functions from \texttt {model.py}}{2} -\contentsline {subsubsection}{\numberline {2.1.1}Core functions:}{2} -\contentsline {subsubsection}{\numberline {2.1.2}Creating regions}{3} -\contentsline {subsubsection}{\numberline {2.1.3}Adding reaction rules to the model}{5} -\contentsline {subsection}{\numberline {2.2}Functions from \texttt {gfrdbase.py}}{8} -\contentsline {subsubsection}{\numberline {2.2.1}Core functions}{8} -\contentsline {subsubsection}{\numberline {2.2.2}Handling objects (surfaces, cylinders, etc)}{9} -\contentsline {subsubsection}{\numberline {2.2.3}Adding particles}{9} -\contentsline {subsubsection}{\numberline {2.2.4}Obtaining information about particles and species}{10} -\contentsline {subsection}{\numberline {2.3}Functions from \texttt {egfrd.py}}{11} -\contentsline {subsubsection}{\numberline {2.3.1}Core functions}{11} -\contentsline {subsubsection}{\numberline {2.3.2}Simulator time (manipulation) functions}{12} -\contentsline {subsubsection}{\numberline {2.3.3}Get data}{12} -\contentsline {subsubsection}{\numberline {2.3.4}Get position and distance data}{12} -\contentsline {subsubsection}{\numberline {2.3.5}Additional functions}{13} -\contentsline {subsection}{\numberline {2.4}Functions from \texttt {dumper.py}}{13} -\contentsline {subsubsection}{\numberline {2.4.1}Getting information on species/particles}{13} -\contentsline {subsubsection}{\numberline {2.4.2}Get information on domains}{16} -\contentsline {subsubsection}{\numberline {2.4.3}Get information on reaction rules}{16} -\contentsline {subsection}{\numberline {2.5}Functions from \texttt {utils.py}}{17} -\contentsline {subsubsection}{\numberline {2.5.1}Mathematical comparisons}{17} -\contentsline {subsubsection}{\numberline {2.5.2}Conversions}{18} -\contentsline {subsubsection}{\numberline {2.5.3}Some convenient functoins}{22} -\contentsline {subsection}{\numberline {2.6}Notes on other files}{23} -\contentsline {subsubsection}{\numberline {2.6.1}\texttt {bd.py}: Brownian Dynamic Simulator}{23} -\contentsline {subsubsection}{\numberline {2.6.2}\texttt {gillespie.py}: Gillespie Simulator}{23} -\contentsline {subsubsection}{\numberline {2.6.3}\texttt {legacy.py}: Old redundant functions}{23} -\contentsline {subsubsection}{\numberline {2.6.4}\texttt {multi.py}, \texttt {pair.py}, \texttt {single.py}}{23} -\contentsline {subsubsection}{\numberline {2.6.5}\texttt {myrandom.py}}{23} -\contentsline {subsubsection}{\numberline {2.6.6}\texttt {make\_cjy\_table.py.py}, \texttt {make\_sjy\_table.py.py}}{23} -\contentsline {subsection}{\numberline {2.7}Function from module \texttt {logger.py}}{23} -\contentsline {subsubsection}{\numberline {2.7.1}HDF5 logger}{23} -\contentsline {subsubsection}{\numberline {2.7.2}"Normal" logger}{24} -\contentsline {section}{\numberline {3}Todo}{24} -\contentsline {section}{\numberline {4}On C++ data structures}{24} -\contentsline {subsection}{\numberline {4.1}SpeciesInfo}{24} -\contentsline {subsection}{\numberline {4.2}Particle}{25} diff --git a/dumper.py b/dumper.py index 4737fe86..7f11eab5 100644 --- a/dumper.py +++ b/dumper.py @@ -66,14 +66,16 @@ def dump_species_names(sim): return ' '.join(get_species_names(sim)) def _get_species_type_by_name(sim, name): - """ Return the type of a species with a certain name - - Arguments: - - sim - an EGFRDSimulator - - name - species name - """ #TODO: Added by wehrens@AMOLF.nl; Please revise. + # Return the type of a species with a certain name + # (Function not intended for public use.) + # + # Arguments: + # - sim + # an EGFRDSimulator + # - name + # species name + # + #TODO: Added by wehrens@AMOLF.nl; Please revise. for species_type in sim.world.model.species_types: if species_type['name'] == name: return species_type @@ -81,21 +83,23 @@ def _get_species_type_by_name(sim, name): raise RuntimeError('SpeciesType %s does not exist.' % (name)) def _get_particles_by_sid(sim, sid): - """ Return a generator (using "yield") to loop over (pid, particle). - - Arguments: - - sim - an EGFRDSimulator - - sid - ID of a species - - E.g.: - - myparticles = _get_particles_by_sid(sim, sid) - - for mypid, myparticle in myparticles: - print str(str(mypid), str(myparticle)) - """ #TODO: Added by wehrens@AMOLF.nl; Please revise. + # Return a generator (using "yield") to loop over (pid, particle). + # (Function not intended for public use.) + # + # Arguments: + # - sim + # an EGFRDSimulator + # - sid + # ID of a species + # + # E.g.: + # + # myparticles = _get_particles_by_sid(sim, sid) + # + # for mypid, myparticle in myparticles: + # print str(str(mypid), str(myparticle)) + # + #TODO: Added by wehrens@AMOLF.nl; Please revise. for pid in sim.world.get_particle_ids(sid): particle = sim.world.get_particle(pid)[1] yield (pid, particle) @@ -142,16 +146,17 @@ def dump_particles(sim, identifier=None): return '\n'.join((str(x) for x in get_particles(sim, identifier))) def _get_number_of_particles_by_sid(sim, sid): - """ - Returns the number of particles of a certain species. - - Arguments: - - sim - an EGFRDSimulator. - - sid - ID of a species + # Returns the number of particles of a certain species. + # (Not intended for public use.) + # + # Arguments: + # - sim + # an EGFRDSimulator. + # - sid + # ID of a species + # + #TODO: Added by wehrens@AMOLF.nl; Please revise. - """ #TODO: Added by wehrens@AMOLF.nl; Please revise. if isinstance(sim, EGFRDSimulator): return len(sim.world.get_particle_ids(sid)) else: @@ -226,7 +231,7 @@ def get_domains(egfrdsim): yield ((did, domain), pid_particle_pair_list, shell_list) def dump_domains(egfrdsim): - """Return an string containing the protective domains in the + """Return a string containing the protective domains in the simulator. """ @@ -277,10 +282,11 @@ def get_reaction_rules(model_or_simulator): return reaction_rules_1, reaction_rules_2, repulsive_rules def _dump_reaction_rule(model, reaction_rule): - """Helper. Return ReactionRule as string. - - ReactionRule.__str__ would be good, but we are actually getting a - ReactionRuleInfo or ReactionRuleCache object.""" + # Helper. Return ReactionRule as string. + # (Not intended for public use.) + # + # ReactionRule.__str__ would be good, but we are actually getting a + # ReactionRuleInfo or ReactionRuleCache object. buf = ('k=%.3g' % reaction_rule.k + ': ').ljust(15) for index, sid in enumerate(reaction_rule.reactants): if index != 0: diff --git a/gfrdbase.py b/gfrdbase.py index 9d46ee09..a0c0fd98 100644 --- a/gfrdbase.py +++ b/gfrdbase.py @@ -351,7 +351,7 @@ def get_first_pid(self, sid): def get_position(self, object): """ Function that returns particle position. Can take multiple - sort of arguments as input. + sorts of arguments as input. Arguments: - object: can be: From ae1b1167dd0a04802bd453bdfc8a3da77e89a902 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Wed, 18 May 2011 14:14:55 +0200 Subject: [PATCH 0047/1541] Fixed bug in get_neighbors_within_radius_no_sort. get_neighbors_within_radius_no_sort resulted in a crash when there are no containers. resolved by adding a try and pass statement. ~MW --- egfrd.py | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/egfrd.py b/egfrd.py index f2df374b..793e43f9 100644 --- a/egfrd.py +++ b/egfrd.py @@ -525,6 +525,20 @@ def burst_objs(self, objs): return bursted def clear_volume(self, pos, radius, ignore=[]): + """ Burst domains within a certain volume and give their ids. + + This function actually has a confusing name, as it only bursts + domains within a certain radius, and gives their ids. It doesn't + remove the particles or something like that. + + (Bursting means it propagates the particles within the domain until + the current time, and then creates a new, minimum-sized domain.) + + Arguments: + - pos: position of area to be "bursted" + - radius: radius of area to be "bursted" + - ignore: domains that should be ignored, none by default. + """ # Added by wehrens@amolf.nl; please revise neighbors = self.get_neighbors_within_radius_no_sort(pos, radius, ignore) return self.burst_objs(neighbors) @@ -1467,16 +1481,19 @@ def get_neighbors_within_radius_no_sort(self, pos, radius, ignore=[]): # shells. Can for example be used to try to clear all objects # from a certain volume. - for container in self.containers: - result = container.get_neighbors_within_radius(pos, radius) - # result = [((shell_id_shell_pair), distance), ] - # Since a domain can have more than 1 shell (multis for - # example), and for each shell there is an entry in the - # shell container, we make sure each domain occurs only once - # in the returned list here. - for did in uniq(s[0][1].did for s in result): - if did not in ignore: - yield self.domains[did] + try: + for container in self.containers: + result = container.get_neighbors_within_radius(pos, radius) + # result = [((shell_id_shell_pair), distance), ] + # Since a domain can have more than 1 shell (multis for + # example), and for each shell there is an entry in the + # shell container, we make sure each domain occurs only once + # in the returned list here. + for did in uniq(s[0][1].did for s in result): + if did not in ignore: + yield self.domains[did] + except AttributeError: + pass def get_intruders(self, position, radius, ignore): intruders = [] # intruders are domains within radius From 3d3b07d63ecd7e828d87385cec48c666de6e66a1 Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Wed, 18 May 2011 14:18:54 +0200 Subject: [PATCH 0048/1541] Updated rebind sample and interface function list. --- doc/interface_functions.tex | 20 ++++- samples/rebind/run.py | 156 ++++++++++++++---------------------- 2 files changed, 81 insertions(+), 95 deletions(-) diff --git a/doc/interface_functions.tex b/doc/interface_functions.tex index 9d25c1f4..d0a65b31 100644 --- a/doc/interface_functions.tex +++ b/doc/interface_functions.tex @@ -14,7 +14,7 @@ \section{Introduction} This document gives an overview of functions that are usefull to end-users of the enhanced Green's Functions Reaction Dynamics (eGFRD) algorithm. In other words, it lists the "interface functions". Basically, this entails a list of function definition plus their so-called python "docstrings", obtained straight from the code. -Note that functions starting with an underscore are actually C++ functions ported to python by boost. +% Note that functions starting with an underscore are actually C++ functions ported to python by boost. \section{Functions} @@ -612,6 +612,24 @@ \subsubsection{Get position and distance data} \end{verbatim} +\subsubsection{Manipulating the system} +\begin{verbatim} + def clear_volume(self, pos, radius, ignore=[]): + """ Burst domains within a certain volume and give their ids. + + This function actually has a confusing name, as it only bursts + domains within a certain radius, and gives their ids. It doesn't + remove the particles or something like that. + + (Bursting means it propagates the particles within the domain until + the current time, and then creates a new, minimum-sized domain.) + + Arguments: + - pos: position of area to be "bursted" + - radius: radius of area to be "bursted" + - ignore: domains that should be ignored, none by default. + """ +\end{verbatim} \subsubsection{Additional functions} diff --git a/samples/rebind/run.py b/samples/rebind/run.py index 31d37fd9..e79489a5 100644 --- a/samples/rebind/run.py +++ b/samples/rebind/run.py @@ -1,23 +1,15 @@ #!/usr/bin/env python -#TODO -""" -See README for a description - -# [N_B] [N_X] [N] - -LOGLEVEL=ERROR PYTHONPATH=../.. python -O run.py 1 100 10 - -""" +# See README for a description +import sys from egfrd import * from bd import * -import sys import gfrdbase import model -def run(outfilename, N_B, N_X, T_list, N): +def run(outfilename, N_B, N_X, N, T_list): """ Function that loops over single runs of binding/rebinding simulations. (Plus some overhead code.) @@ -57,14 +49,9 @@ def run(outfilename, N_B, N_X, T_list, N): matrix_size = min(max(3, int((9 * (N_X+N_B)) ** (1.0/3.0))), 60) print 'matrix_size=', matrix_size - # Overrides the T_list input - T_list = [tau*0.1, tau, tau*10, tau * 100, tau * 1000, INF] - T_list = range(0, int(tau*100000), 100) - T_list.append(INF) - - # Clear the distance output file + # Clear the distance output files for j in T_list: - outfile_r = open(outfilename + '_r_-'+str(j)+'.dat', 'a') + outfile_r = open(outfilename + '_r_'+str(j)+'.dat', 'a') outfile_r.close() # Open the time output file @@ -164,38 +151,35 @@ def singlerun(T_list, N_B, N_X): B_pos = [(float(A['radius']) + float(B['radius']))+1e-23,0,0] # Clear an area at position A_pos and place particle A there - """ - What happens here: - - find particles at position A_pos within species A radius - - delete those - - add a number of X particles _randomly to the box_, the - number being equal to # removed particles. - - if a particle is accidently placed within the "cleared" - area, repeat the process (in very crowded box, this - leads to infinite loop) - - - - """#TODO - """This code doesn't function properly, because you want to clear - particles within a certain radius.. Now you can be removing particles - that after bursting are not in the area anymore, but whose bursted - domains were. - - After bursting you should just check whether something is within the radius. - - Perhaps it would be more easy to just place all the particles at - the beginning, but give A and B initially a diffusion constant of - 0, then stirr, and then set the diffusion constant to their - desired values..""" + # + # How this should be done: + # - find particles at position A_pos within species A radius + # - delete those + # - add a number of X particles _randomly to the box_, the + # number being equal to # removed particles. + # - if a particle is accidently placed within the "cleared" + # area, repeat the process (in very crowded box, this + # leads to infinite loop) + # + # Currently, however, it doesn't get the particles within a certain radius, + # but the particles which domain lies within a certain radius. + # + # Therefore, you might be removing particles that after bursting are not + # within the radius anymore, but whose (now bursted) domains were. + # + # TODO + # This code should thus be adapted to solve this problem. + # + # Perhaps it would be more easy to just place all the particles at + # the beginning, but give A and B initially a diffusion constant of + # 0, then stirr, and then set the diffusion constant to their + # desired values..""" while 1: + # Burst domains within a certain volume and collect their ids + dd = s.clear_volume(A_pos, float(A['radius'])) - #pp = s.get_particles_within_radius(A_pos, float(A['radius'])) - dd = s.clear_volume(A_pos, float(A['radius'])) # returns a list of - # domains ID's that - # where bursted within - # volume if not dd: break for d in dd: @@ -208,10 +192,10 @@ def singlerun(T_list, N_B, N_X): # Idem for a particle B: while 1: - dd = s.clear_volume(B_pos, float(B['radius'])) # returns a list of - # domains ID's that - # where bursted within - # volume + + # Burst domains within a certain volume and collect their ids + dd = s.clear_volume(B_pos, float(B['radius'])) + if not dd: break for d in dd: @@ -239,31 +223,32 @@ def singlerun(T_list, N_B, N_X): # ### Start simulating while 1: - """ - What happens in this if-statement: - Create a list of the times particles were in bound state. - - If there was a reaction: - - Binding: Indicated by the fact that there are no C-particles. - In this case: record the current time (s.t) to t_last. - - Unbinding: Indicated because this is the only other case when - a reaction has taken place. - In this case: record the time binding lasted, i.e. dt between - current time (s.t) and last reaction time (t_last). - """ + + # What happens in the following if-statement: + # Create a list of the durations particles were in bound state. + # + # If there was a reaction: + # - Binding: Indicated by the fact that there are no C-particles. + # In this case: record the current time (s.t) to t_last. + # - Unbinding: Indicated because this is the only other case when + # a reaction has taken place. + # In this case: record the time binding lasted, i.e. dt between + # current time (s.t) and last reaction time (t_last). if s.last_reaction: # aka if there was a reaction print "* Reaction detected at: " + str(s.last_reaction) if len(s.world.get_particle_ids(C.id)) == 0: #A,B print ' - set t_last', str(s.t) - t_last = s.t # set t_last - else: # C + # set t_last to "current time" in simulator + t_last = s.t + else: print ' - reaction: ', str(s.t - t_last) t_list.append(s.t - t_last) - """ If it's time to log, then log distance between particles. """ + # If it's time to log, then log distance between particles. next_time = s.get_next_time() if next_time > next_stop: - print '* Measuring distances at ', str(i_T), str(next_stop) + print '* Measuring distances at ', str(next_stop),\ + '(measurement #', str(i_T), ')' s.stop(next_stop) print '* stopped' # If there is a particle C, the distance is "0". @@ -290,12 +275,10 @@ def singlerun(T_list, N_B, N_X): next_stop = T_list[i_T] print "* Done, returning to simulation loop." - """ If there are no measuring moments on the list any more, - stop """ - if (next_stop == INF): - print '* Break at ', str(s.t) - if (len(t_list) == 0): - t_list.append(float('nan')) + # If there are no measuring moments on the list any more, and + # a duration of being bounded has been recorded, then stop + if (next_stop == INF) and (len(t_list) != 0): + print 'break', s.t break s.step() @@ -306,30 +289,15 @@ def singlerun(T_list, N_B, N_X): if __name__ == '__main__': import os + + # convert last user arguments to array T_list + T_list = [] + for measurement_time in sys.argv[4:]: + T_list.append(float(measurement_time)) - T_list = eval(sys.argv[3]) - - outfilename = 'data/rebind_' + '_'.join(sys.argv[1:4]) +\ - '_' #+ os.environ['SGE_TASK_ID'] - run(outfilename, float(sys.argv[1]), - int(sys.argv[2]), T_list, int(sys.argv[4])) - - - - -""" - -Some additional notes: - - # 100 nM = 100e-9 * N_A * 100 / m^3 = 6.02e19 - # V = 1 / 6.02e19 = 1.66e-20 m^3 - # L = 2.55e-7 m - - # 1 uM = 6.02e20 / m^3 - # V = 1.66e-21 m^3 - # L = 1.18e-7 + run('data/rebind', float(sys.argv[1]), + int(sys.argv[2]), int(sys.argv[3]), T_list) -""" From c05a9ea0df71586df2467c31ea03a1b0cc9574cc Mon Sep 17 00:00:00 2001 From: Martijn Wehrens Date: Wed, 18 May 2011 14:20:55 +0200 Subject: [PATCH 0049/1541] Rebind sample should now function properly, also added README file. ~MW --- samples/rebind/README | 73 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 samples/rebind/README diff --git a/samples/rebind/README b/samples/rebind/README new file mode 100644 index 00000000..4f7df639 --- /dev/null +++ b/samples/rebind/README @@ -0,0 +1,73 @@ + + + +Introduction +===================================== +This example deals with the rebinding problem. The simplest case of +this probllem is when you have two particles of different species +that can react with each other, say A and B, to a particle of species +C: + +A + B <-> C + +If all these particles have different diffusion constants, one may +wonder how the path of the center of mass of this particle system +can be approximated by a single diffusion constant. + +A major question in this problem is how many time the particles +spend being particles A & B, and how many time the particles spend +as particle C. + +Here, such a system is simulated. There is one particle A, and a +fixed number of particles B, which can react to a particle C. +Additionally there are some particles X that are inert. + +The simulation initially places a particle A and a particle B next +to it. If the number of particles B >1 then the rest of the B-particles +are placed at random positions. (But can still be bound by particle A.) + +Running +===================================== +The simulation can be run as follows: +$ LOGLEVEL=ERROR PYTHONPATH=../.. python -O run.py [N_B] [N_X] [N] [t1, +t2, t3, t4, .., INF] + +Example: +$ LOGLEVEL=ERROR PYTHONPATH=../.. python -O run.py 1 100 10 1.0 2.5 3 4.0 INF + +Here, N_B and N_X are respectively the number of B and X particles. N is the +number of times the simulation is ran. [t1, t2, .., INF] is an array of times +at which the distances between the two particles are logged. (Taken zero if a +set of A and B particles have reacted to a C particle.) This array should +minimally contain the INF value. + If only [INF] is given as input, the simulation will run until it +has recorded a duration for which the particles were in a bound state. +Otherwise, the system will keep going untill all measurement times have +been passed (and then still run until a duration is recorded). + + +Output +===================================== +The most important data is written away to files in the data directory. + +The duration of recorded bindings are written to: +rebind_t.dat + +The distances between the particles logged at times given by the user +are written to: +rebind_r_