{ "cells": [ { "cell_type": "markdown", "id": "46843e2b60855a11", "metadata": {}, "source": [ "# Introduction to the `crunchflow.input` package\n", "\n", "The `crunchflow.input` package is designed to efficiently open, edit and save text files used to run the CrunchFlow reactive transport code. While it is possible (and often simpler) to perform CrunchFlow simulations by opening input files in a text editor and editing them manually, this workflow can be too time-consuming for tasks that require many simulations, such as sensitivity analyses. Generating several hundred (or several thousand) input files by hand simply isn't practical. This package provides classes for creating and editing these files programmatically. " ] }, { "cell_type": "markdown", "id": "b92c7d3d1a178cdf", "metadata": {}, "source": [ "## 1. Creating an `InputFile` object\n", "\n", "The `InputFile` class creates objects that represent CrunchFlow input files. These objects have methods for adding, removing, and modifying sections and parameters, as well as saving them to a file. \n", "\n", "### 1a. Keyword Blocks\n", "Each `InputFile` object contains keyword blocks (`KeywordBlock` objects) that correspond to individual blocks within a CrunchFlow input file. All the standard blocks that CrunchFlow recognizes (`PRIMARY_SPECIES`, `RUNTIME`, `FLOW`, etc) are available as classes in the `crunchflow.input.blocks` module. To adhere to Python naming conventions, `KeywordBlock` objects are always defined in CamelCase (i.e., `PrimarySpecies`, `Runtime`, `Flow`, etc)." ] }, { "cell_type": "code", "id": "bf5d3b25b67fa669", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.122091Z", "start_time": "2025-06-01T11:35:21.114025Z" } }, "source": [ "# Import the Runtime block\n", "from crunchflow.input.blocks import Runtime\n", "\n", "# Create an instance of Runtime block\n", "runtime_block = Runtime()\n", "\n", "# It's possible to create blocks by defining individual attributes\n", "runtime_block.time_units = \"years\"\n", "runtime_block.timestep_max = 0.01\n", "runtime_block.time_tolerance = 0.001\n", "runtime_block.gimrt = True\n", "\n", "# Or using the set_parameters method, which takes a dictionary\n", "# as input, where the keys are the attribute names and the values\n", "# are the attribute values\n", "runtime_block.set_parameters({\"time_units\": \"years\", \"timestep_max\": 0.01, \"time_tolerance\": 0.001, \"gimrt\": True})\n", "\n", "print(runtime_block)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "gimrt true\n", "timestep_max 0.01\n", "time_tolerance 0.001\n", "time_units years\n" ] } ], "execution_count": 1 }, { "cell_type": "markdown", "id": "43ce5f23ab80bf65", "metadata": {}, "source": [ "### 1b. Adding Blocks to an InputFile\n", "\n", "In Python, objects have attributes. For an `InputFile` these attributes are keyword blocks. While `KeywordBlock` objects are defined in CamelCase (e.g., `PrimarySpecies`, `Runtime`, `Flow`, etc), the corresponding `InputFile` attributes are all lower case, using an underscore to separate words (i.e., `InputFile.primary_species`, . `InputFile.runtime`, `InputFile.flow`, etc). \n", "\n", "To add a block to an `InputFile`, simply set the attribute to the block you want to add. For example, to add a `Runtime` block to an `InputFile`:" ] }, { "cell_type": "code", "id": "25a5b819ba25559", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.173784Z", "start_time": "2025-06-01T11:35:21.169071Z" } }, "source": [ "# Import the InputFile class\n", "from crunchflow.input import InputFile\n", "\n", "# Create an instance of the InputFile class\n", "my_simulation = InputFile()\n", "\n", "# Set the runtime attribute for this input file\n", "my_simulation.runtime = runtime_block\n", "\n", "# Printing the InputFile shows all defined blocks for that\n", "# individual simulation\n", "print(my_simulation)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "RUNTIME\n", "gimrt true\n", "timestep_max 0.01\n", "time_tolerance 0.001\n", "time_units years\n", "END\n" ] } ], "execution_count": 2 }, { "cell_type": "markdown", "id": "14ac64cb95da2f18", "metadata": {}, "source": [ "## 2. Loading an `InputFile` object from file \n", "\n", "While it's possible to define an `InputFile` object from scratch, it's often more convenient to load an existing input file and modify it. This can be done using the `InputFile.load` method. As an example, let's load a sample input file from the CrunchFlow short course:" ] }, { "cell_type": "code", "id": "9053fb7187e00936", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.277014Z", "start_time": "2025-06-01T11:35:21.273194Z" } }, "source": [ "# Load an existing input file\n", "my_simulation = InputFile.load(\"surface_complexation.in\", path=\"input_files\")\n", "\n", "# If we print this object, we can see that it contains all the blocks\n", "# and parameters from the input file\n", "print(my_simulation)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "TITLE\n", "Problem 3: multi-component surface complexation\n", "END\n", "\n", "RUNTIME\n", "correction_max 2.0\n", "database datacom.dbs\n", "database_sweep false\n", "debye-huckel true\n", "gimrt true\n", "screen_output 100\n", "speciate_only false\n", "timestep_max .1\n", "timestep_init 1.0E-14\n", "time_tolerance 0.005\n", "time_units years\n", "END\n", "\n", "OUTPUT\n", "spatial_profile 1 4\n", "time_series_print H+ Tracer SiO2(aq) Na+ Ca++ CO2(aq) pH UO2++ Zn++ Pb++ Hg++\n", "time_series totconhistory1.txt 100 1 1\n", "time_series totconhistory2.txt 300 1 1\n", "END\n", "\n", "DISCRETIZATION\n", "xzones 400 0.25\n", "END\n", "\n", "FLOW\n", "calculate_flow true\n", "distance_units meters\n", "time_units years\n", "pressure 300000 default\n", "pressure 300000 zone 0-0 1-1 1-1 fix\n", "pressure 0 zone 401-401 1-1 1-1 fix\n", "permeability_x 1.0E-13 default\n", "END\n", "\n", "TRANSPORT\n", "cementation_exponent 1.0\n", "dispersivity 10\n", "distance_units centimeters\n", "fix_diffusion 0.919E-05\n", "formation_factor 1.0\n", "time_units seconds\n", "END\n", "\n", "PRIMARY_SPECIES\n", "H+\n", "CO2(aq)\n", "Mg++\n", "Ca++\n", "Na+\n", "Fe+++\n", "SiO2(aq)\n", "Cl-\n", "UO2++\n", "Zn++\n", "Pb++\n", "Hg++\n", "Tracer\n", "END\n", "\n", "SECONDARY_SPECIES\n", "(UO2)2(OH)2++\n", "(UO2)2OH+++\n", "(UO2)3(OH)4++\n", "(UO2)3(OH)5+\n", "(UO2)3(OH)7-\n", "(UO2)4(OH)7+\n", "HCO3-\n", "CaCl+\n", "CaCl2(aq)\n", "CaOH+\n", "Fe(OH)2+\n", "Fe(OH)3(aq)\n", "Fe(OH)4-\n", "Fe2(OH)2++++\n", "Fe3(OH)4(5+)\n", "FeCl++\n", "FeCl2+\n", "FeCl4-\n", "FeOH++\n", "H2SiO4--\n", "H4(H2SiO4)4----\n", "H6(H2SiO4)4--\n", "HCl(aq)\n", "HSiO3-\n", "Mg4(OH)4++++\n", "MgCl+\n", "NaCl(aq)\n", "NaHSiO3(aq)\n", "NaOH(aq)\n", "OH-\n", "Pb(OH)2(aq)\n", "Pb(OH)3-\n", "Pb2OH+++\n", "Pb3(OH)4++\n", "Pb4(OH)4++++\n", "Pb6(OH)8++++\n", "PbCl+\n", "PbCl2(aq)\n", "PbCl3-\n", "PbCl4--\n", "PbOH+\n", "UO2(OH)2(aq)\n", "UO2(OH)3-\n", "UO2(OH)4--\n", "UO2Cl+\n", "UO2Cl2(aq)\n", "UO2OH+\n", "Zn(OH)2(aq)\n", "Zn(OH)3-\n", "Zn(OH)4--\n", "Zn(OH)Cl(aq)\n", "ZnCl+\n", "ZnCl2(aq)\n", "ZnCl3-\n", "ZnCl4--\n", "ZnOH+\n", "(UO2)11(CO3)6(OH)12--\n", "(UO2)2CO3(OH)3-\n", "(UO2)3(CO3)6(6-)\n", "(UO2)3(OH)5CO2+\n", "(UO2)3O(OH)2(HCO3)+\n", "CO3--\n", "CaCO3(aq)\n", "CaHCO3+\n", "FeCO3+\n", "MgCO3(aq)\n", "MgHCO3+\n", "NaCO3-\n", "NaHCO3(aq)\n", "Pb(CO3)2--\n", "PbCO3(aq)\n", "UO2(CO3)2--\n", "UO2(CO3)3----\n", "UO2CO3(aq)\n", "ZnCO3(aq)\n", "ZnHCO3+\n", "END\n", "\n", "MINERALS\n", "Fe(OH)3 -rate -55 -!set to -be non-reactive\n", "Quartz -rate -55 -!set to -be non-reactive\n", "END\n", "\n", "GASES\n", "CO2(g)\n", "END\n", "\n", "INITIAL_CONDITIONS\n", "Groundwater 1-400\n", "END\n", "\n", "BOUNDARY_CONDITIONS\n", "x_begin Minewater flux\n", "x_end Groundwater flux\n", "y_begin Groundwater flux\n", "y_end Groundwater flux\n", "END\n", "\n", "SURFACE_COMPLEXATION\n", ">FeOH_strong on Fe(OH)3\n", "END\n", "\n", "CONDITION Minewater\n", "temperature 25\n", "pH 8.5\n", "CO2(aq) CO2(g) 0.001\n", "Mg++ 1E-4\n", "Ca++ 9E-5\n", "Na+ charge\n", "Fe+++ 1E-19\n", "SiO2(aq) 1E-7\n", "Cl- 1E-4\n", "Tracer 1E-4\n", "UO2++ 1E-4\n", "Zn++ 1E-4\n", "Pb++ 1E-4\n", "Hg++ 1E-4\n", "Fe(OH)3 1E-20 ssa 1e-10\n", ">FeOH_strong 1E-20\n", "END\n", "\n", "CONDITION Groundwater\n", "temperature 25\n", "pH 7.5\n", "CO2(aq) CO2(g) 0.0001\n", "Mg++ 1E-4\n", "Ca++ 9E-5\n", "Na+ 1E-4\n", "Fe+++ 1E-19\n", "SiO2(aq) 1E-7\n", "Cl- charge\n", "Tracer 1E-30\n", "UO2++ 1E-9 equilibrate_surface\n", "Zn++ 1E-9 equilibrate_surface\n", "Pb++ 1E-9 equilibrate_surface\n", "Hg++ 1E-9 equilibrate_surface\n", "Quartz 0.700 ssa 1\n", "Fe(OH)3 0.0005 ssa 600.0 surface\n", ">FeOH_strong 9.259259E-08\n", "END\n", "\n", "TEMPERATURE\n", "set_temperature 25\n", "END\n" ] } ], "execution_count": 3 }, { "cell_type": "code", "id": "ee097af559c2b50d", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.328108Z", "start_time": "2025-06-01T11:35:21.326024Z" } }, "source": [ "# Now the attributes of the InputFile object are the blocks\n", "# from the input file. We can access these blocks using dot notation\n", "runtime_block = my_simulation.runtime\n", "\n", "print(runtime_block)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "correction_max 2.0\n", "database datacom.dbs\n", "database_sweep false\n", "debye-huckel true\n", "gimrt true\n", "screen_output 100\n", "speciate_only false\n", "timestep_max .1\n", "timestep_init 1.0E-14\n", "time_tolerance 0.005\n", "time_units years\n" ] } ], "execution_count": 4 }, { "cell_type": "code", "id": "c5e6b9621c3e326c", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.383300Z", "start_time": "2025-06-01T11:35:21.379472Z" } }, "source": [ "# We can also print individual attributes of a block\n", "print(runtime_block.time_units)\n", "\n", "# We can also string these together to access nested attributes\n", "print(my_simulation.runtime.time_units)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "years\n", "years\n" ] } ], "execution_count": 5 }, { "cell_type": "markdown", "id": "aaa1a20f25baa4ed", "metadata": {}, "source": [ "### 2a. `Conditions` blocks\n", "\n", "Some blocks are a little more complicated than others. For example, an individual CrunchFlow input file can have multiple `Conditions` blocks. To accommodate this, the `InputFile.conditions` attribute is a dictionary of multiple `KeywordBlock` objects, where the keys are the condition names. For example:" ] }, { "cell_type": "code", "id": "3c17523235ab293b", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.439916Z", "start_time": "2025-06-01T11:35:21.436998Z" } }, "source": [ "# Print which conditions are available in this input file\n", "print(my_simulation.conditions)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'Minewater': Minewater, 'Groundwater': Groundwater}\n" ] } ], "execution_count": 6 }, { "cell_type": "code", "id": "a4ec85e4d09d9817", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.493968Z", "start_time": "2025-06-01T11:35:21.491122Z" } }, "source": [ "# Print the 'Minewater' condition using dictionary notation\n", "print(my_simulation.conditions[\"Minewater\"])" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CONDITION Minewater\n", "temperature 25\n", "pH 8.5\n", "CO2(aq) CO2(g) 0.001\n", "Mg++ 1E-4\n", "Ca++ 9E-5\n", "Na+ charge\n", "Fe+++ 1E-19\n", "SiO2(aq) 1E-7\n", "Cl- 1E-4\n", "Tracer 1E-4\n", "UO2++ 1E-4\n", "Zn++ 1E-4\n", "Pb++ 1E-4\n", "Hg++ 1E-4\n", "Fe(OH)3 1E-20 ssa 1e-10\n", ">FeOH_strong 1E-20\n" ] } ], "execution_count": 7 }, { "cell_type": "markdown", "id": "2420562a5ea7d94e", "metadata": {}, "source": "Within a `Conditions` block, there are some conventional attributes (such as `temperature`, `set_porosity`, etc.). The concentrations of individual species are stored in a dictionary called `concentrations`. " }, { "cell_type": "code", "id": "cff3e17f70bab1a4", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.546311Z", "start_time": "2025-06-01T11:35:21.543201Z" } }, "source": [ "# Print the concentrations of each species within the `Minewater` condition\n", "# The nested dictionary notation here is a little verbose, but\n", "# it shows that the attributes of an object can have attributes themselves\n", "for species, concentration in my_simulation.conditions[\"Minewater\"].concentrations.items():\n", " print(f\"{species}: {concentration}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "pH: 8.5\n", "CO2(aq): CO2(g) 0.001\n", "Mg++: 1E-4\n", "Ca++: 9E-5\n", "Na+: charge\n", "Fe+++: 1E-19\n", "SiO2(aq): 1E-7\n", "Cl-: 1E-4\n", "Tracer: 1E-4\n", "UO2++: 1E-4\n", "Zn++: 1E-4\n", "Pb++: 1E-4\n", "Hg++: 1E-4\n", "Fe(OH)3: 1E-20 ssa 1e-10\n", ">FeOH_strong: 1E-20\n" ] } ], "execution_count": 8 }, { "cell_type": "markdown", "id": "674a1e133b13551e", "metadata": {}, "source": [ "### 2b. The `KineticsBlock` sub-class\n", "\n", "Another complicated type of block are those that include information on reaction kinetics (`MINERALS` and `AQUEOUS_KINETICS`). These are all read into a `KineticsBlock` sub-class that consists of nested dictionaries. Within the outer dictionary, the key is the species and within the inner dictionary, the key is the reaction label. For example, a `MINERALS` block can be listed as follows:\n", "\n", "```\n", "MINERALS\n", "Calcite\n", "Barite -label default -rate -4.5 -activation 1.0\n", "Barite -label h+ -rate -2.5 -activation 0.0\n", "END\n", "```\n", "\n", "So, the reaction dictionary for \"Barite\" would be `{'default': {'rate': -4.5, 'activation': 1.0}, \n", " 'h+': {'rate': -2.5, 'activation': 0.0}}`. " ] }, { "cell_type": "code", "id": "910b72c4489226e3", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.606018Z", "start_time": "2025-06-01T11:35:21.603113Z" } }, "source": [ "# Let's test the above functionality with an example input file\n", "my_simulation = InputFile.load(\"minerals_example.in\", path=\"input_files\")\n", "print(my_simulation.minerals)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Calcite \n", "Barite -rate -4.5 -activation 1.0\n", "Barite -label h+ -rate -2.5 -activation 0.0\n" ] } ], "execution_count": 9 }, { "cell_type": "code", "id": "f4d72fc9639086ea", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.662965Z", "start_time": "2025-06-01T11:35:21.659398Z" } }, "source": [ "# If we print the reaction dictionary for Barite, it should be what we showed above\n", "print(my_simulation.minerals.species_dict[\"Barite\"])" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'default': {'rate': '-4.5', 'activation': '1.0'}, 'h+': {'rate': '-2.5', 'activation': '0.0'}}\n" ] } ], "execution_count": 10 }, { "cell_type": "code", "id": "c181e9df9f6ca37e", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.711677Z", "start_time": "2025-06-01T11:35:21.708563Z" } }, "source": [ "# And for calcite, it's a lot shorter\n", "# Note that if no reaction label is provided, the label is assumed to be 'default'\n", "print(my_simulation.minerals.species_dict[\"Calcite\"])" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'default': {}}\n" ] } ], "execution_count": 11 }, { "cell_type": "markdown", "id": "9525d8bf78c67090", "metadata": {}, "source": [ "### 2c. The `other` attribute\n", "\n", "Sometimes, the `load` method might read in an attribute that is not recognized by the `crunchflow` package. In this case, `load` store this information in the `other` attribute of the block. It will still be written to file and can be modified, but a warning will be issued. For example:\n" ] }, { "cell_type": "code", "id": "269ff710a4e01077", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.762508Z", "start_time": "2025-06-01T11:35:21.758755Z" } }, "source": [ "# Load an input file\n", "# If you open the file, you'll see that the RUNTIME block\n", "# contains an attribute called 'new_crunch_feature'\n", "my_simulation = InputFile.load(\"surface_complexation_modified.in\", path=\"input_files\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\tWarning: Unrecognized attribute 'new_crunch_feature' in block 'runtime'\n" ] } ], "execution_count": 12 }, { "cell_type": "code", "id": "7c4f7bd6115edfb5", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.811025Z", "start_time": "2025-06-01T11:35:21.808116Z" } }, "source": [ "# Despite the warning, new_crunch_feature is still printed in\n", "# the block and can be modified\n", "print(my_simulation.runtime)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "correction_max 2.0\n", "database datacom.dbs\n", "database_sweep false\n", "debye-huckel true\n", "gimrt true\n", "screen_output 100\n", "speciate_only false\n", "timestep_max .1\n", "timestep_init 1.0E-14\n", "time_tolerance 0.005\n", "time_units years\n", "new_crunch_feature true\n" ] } ], "execution_count": 13 }, { "cell_type": "markdown", "id": "95bd93d8ed0c2a03", "metadata": {}, "source": [ "## 3. Loading an `InputFile`, modifying it and saving it to file \n", "\n", "We can combine all these various methods to load an input file, modify it, and save it to a new file. For example, let's load the surface complexation example, change the pH of the influent (\"Minewater\"), and save it to a new file:" ] }, { "cell_type": "code", "id": "efc4f3f764ccf7ef", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.863230Z", "start_time": "2025-06-01T11:35:21.859593Z" } }, "source": [ "# Load an existing input file\n", "my_simulation = InputFile.load(\"surface_complexation.in\", path=\"input_files\")\n", "\n", "# To limit too many nested dictionaries/attributes, assign\n", "# the minewater condition to a variable and print it\n", "minewater_cond = my_simulation.conditions[\"Minewater\"]\n", "print(minewater_cond)" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CONDITION Minewater\n", "temperature 25\n", "pH 8.5\n", "CO2(aq) CO2(g) 0.001\n", "Mg++ 1E-4\n", "Ca++ 9E-5\n", "Na+ charge\n", "Fe+++ 1E-19\n", "SiO2(aq) 1E-7\n", "Cl- 1E-4\n", "Tracer 1E-4\n", "UO2++ 1E-4\n", "Zn++ 1E-4\n", "Pb++ 1E-4\n", "Hg++ 1E-4\n", "Fe(OH)3 1E-20 ssa 1e-10\n", ">FeOH_strong 1E-20\n" ] } ], "execution_count": 14 }, { "cell_type": "code", "id": "d74c2400b94a112", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.916744Z", "start_time": "2025-06-01T11:35:21.914269Z" } }, "source": [ "# Now change the pH of this variable\n", "# Because pH is stored in the `concentrations` attribute of a `Conditions`\n", "# block, we'll have to set it using dictionary notation\n", "minewater_cond.concentrations[\"pH\"] = 8.0\n", "\n", "# Now print the modified condition\n", "# (Note that in Python, variable names are just references to\n", "# objects. You can think of them like aliases. So when we modify\n", "# minewater_cond, we also modify the original object. Thus,\n", "# my_simulation.conditions['Minewater'] should reflect the new pH.)\n", "print(my_simulation.conditions[\"Minewater\"])" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CONDITION Minewater\n", "temperature 25\n", "pH 8.0\n", "CO2(aq) CO2(g) 0.001\n", "Mg++ 1E-4\n", "Ca++ 9E-5\n", "Na+ charge\n", "Fe+++ 1E-19\n", "SiO2(aq) 1E-7\n", "Cl- 1E-4\n", "Tracer 1E-4\n", "UO2++ 1E-4\n", "Zn++ 1E-4\n", "Pb++ 1E-4\n", "Hg++ 1E-4\n", "Fe(OH)3 1E-20 ssa 1e-10\n", ">FeOH_strong 1E-20\n" ] } ], "execution_count": 15 }, { "cell_type": "code", "id": "ffe2ed764a091f5a", "metadata": { "ExecuteTime": { "end_time": "2025-06-01T11:35:21.971491Z", "start_time": "2025-06-01T11:35:21.968613Z" } }, "source": [ "# Save the modified input file to a new file\n", "my_simulation.save(\"surface_complexation_pH8.in\", path=\"input_files\")" ], "outputs": [], "execution_count": 16 } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "2.7.6" } }, "nbformat": 4, "nbformat_minor": 5 }