Repository: ticdat/ticdat (language: python)

Issue:
remove OPL support and housecleaning issues
Would be nice to update the `setup.py` to add Shannon as a maintainer, since she put `ticdat` on `conda`. (So also, **remind her** that there is a newer ticdat when we make the 0.2.25 release. )

[Kelley.Shannon.patricia@gmail.com](mailto:Kelley.Shannon.patricia@gmail.com)

I think the best we can do here is have both Sam and Shannon emails in the setup.py as a comma delimited text. The PyPi email would be a little messy but people would figure it out. 


**Also,**  remove`oplrun_setup.py` reference from MANIFEST.in file.

**Both of these are short and sweet, but holding off on doing them until connecting with Sam for yearly upgrade**
bad timezone definition for `datetime=True` should trip a data type failure
Execute the following in iPython 

```
x = pd.Timestamp('2024-03-22 18:56:32.738122+46')
x
```

And you can see the following error

```
ValueError: offset must be a timedelta strictly between -timedelta(hours=24) and timedelta(hours=24).
```

This data cell should fail `find_data_type_failures`.  Thats the bug fix here. Instead of passing, this problematic string should fail `find_data_type_failures`.

----- 

Internal implementation detail... 


The fix  is that after you've munged the string successfully to a `datetime` (or `Timestamp)` then you should also be able to call `repr()` on the resulting object without throwing an exception. If `repr` throws an exception then its also a failure. 

Also see #208 for more implementation details.
clone should accept a convert_dat argument to enable all row predicates to convert
This is for `TicDatFactory.clone` and `PanDatFactory.clone`. See the `no_copy_predicate_kwargs_maker` variable. When cloning from one to the other, the `predicate_kwargs_maker` argument row predicates are discarded. 

Ideally, we do something clever so that `find_data_row_failures` doesn't need to call the `convert_dat` more than once. 

See [here](https://github.com/decision-spot/roundoff_helper/issues/180) for more details.
 FutureWarning: Inferring datetime64[ns] from data containing strings is deprecated 
This is triggered in `test_datetime` in `testpandat_io.py`.  But only with the `parameters2.json` file.  I'm reproducing the whole file below for clarity.

```
{
  "parameters": [
    [
      "p1",
      "7/11/1911"
    ],
    [
      "p2",
      null
    ]
  ],
  "table_with_stuffs": [
    [
      "1972-07-11 00:00:00",
      "NaT"
    ],
    [
      "2024-05-21 13:36:52.307234",
      "2011-09-11 00:00:00"
    ]
  ]
}
```

It was weirdly hard to isolate this to something smaller, but its worth the effort. 

At any rate, the warning is raised in `_general_post_read_adjustment` when it calls 

`df[f] = apply(df, fixed_row` inside the ` if dt and dt.datetime:` clause.  

This in turn leads us to `faster_df_apply`, where specifically the line that raises the warning is.

```
return pd.Series(data, index=index, **({"dtype": numpy.float64} if not data else {}))
```

The warning is raised when `data` looks like

```
['NaT', Timestamp('2011-09-11 00:00:00')]
```

So if you put `debug_break() if "NaT" in data else None` in the `faster_df_apply` and then run just `test_datetime` you see it.

You can also see it with
```
import pandas as pd
data = ['NaT', pd.Timestamp('2011-09-11 00:00:00')]
index = [0, 1]
pd.Series(data, index=index)
```

but of course you don't see it with

```
data = [None, pd.Timestamp('2011-09-11 00:00:00')]
pd.Series(data, index=index)
``` 
remove OPL support and housecleaning issues
Would be nice to update the `setup.py` to add Shannon as a maintainer, since she put `ticdat` on `conda`. (So also, **remind her** that there is a newer ticdat when we make the 0.2.25 release. )

[Kelley.Shannon.patricia@gmail.com](mailto:Kelley.Shannon.patricia@gmail.com)

I think the best we can do here is have both Sam and Shannon emails in the setup.py as a comma delimited text. The PyPi email would be a little messy but people would figure it out. 


**Also,**  remove`oplrun_setup.py` reference from MANIFEST.in file.

**Both of these are short and sweet, but holding off on doing them until connecting with Sam for yearly upgrade**
remove OPL support and housecleaning issues
Would be nice to update the `setup.py` to add Shannon as a maintainer, since she put `ticdat` on `conda`. (So also, **remind her** that there is a newer ticdat when we make the 0.2.25 release. )

[Kelley.Shannon.patricia@gmail.com](mailto:Kelley.Shannon.patricia@gmail.com)

I think the best we can do here is have both Sam and Shannon emails in the setup.py as a comma delimited text. The PyPi email would be a little messy but people would figure it out. 


**Also,**  remove`oplrun_setup.py` reference from MANIFEST.in file.

**Both of these are short and sweet, but holding off on doing them until connecting with Sam for yearly upgrade**


Discussion / hints:
Not removing Microsoft Access support yet, since there is some use for it IRL, from what I can tell.
I'm calling this a bug fix. That said, programmers that don't like this new behavior can use a row predicate to perform the previous `datetime=True` check and then also do the munging themselves on the `dat` object. 
**Add some special logic** so that only row predicates  added from inside the clone are the ones that use the `convert_dat`

Not removing Microsoft Access support yet, since there is some use for it IRL, from what I can tell.
Not removing Microsoft Access support yet, since there is some use for it IRL, from what I can tell.

Fix patch:
diff --git a/MANIFEST.in b/MANIFEST.in
index 4ae0b8ae..71e428cf 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -2,5 +2,4 @@ include MANIFEST.in
 include LICENSE
 include README.md
 include setup.py
-include oplrun_setup.py
 include accdb_create_setup.py
diff --git a/examples/expert_section/opl/cp_jobs/create_xlsx_file.py b/examples/expert_section/opl/cp_jobs/create_xlsx_file.py
deleted file mode 100644
index fffbde49..00000000
--- a/examples/expert_section/opl/cp_jobs/create_xlsx_file.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from jobs import input_schema
-dat = input_schema.sql.create_tic_dat_from_sql("jobs_sample_data.sql")
-input_schema.xls.write_file(dat, "Jobs_Sample_Data.xlsx", allow_overwrite=True)
\ No newline at end of file
diff --git a/examples/expert_section/opl/cp_jobs/jobs.mod b/examples/expert_section/opl/cp_jobs/jobs.mod
deleted file mode 100644
index 537cd0c7..00000000
--- a/examples/expert_section/opl/cp_jobs/jobs.mod
+++ /dev/null
@@ -1,94 +0,0 @@
-/*********************************************
- * OPL job scheduling problem organized into tabular, ticdat compliant format
- * See https://ibm.co/2rGVyet for reference problem and sample data.
- *********************************************/
-
-/* ------------------------ begin data initialization section ---------------------- */
-using CP;
-include "ticdat_jobs.mod";
-
-/* Tasks and 'areaA', 'areaS' are hard coded into the mathematics section,
- * so we hard code them here as well
- */
-
-{string} Jobs = {j | <j,m1,d1,m2,d2> in inp_jobs};
-{string} Tasks  = {"loadA","unload1","process1","load1","unload2","process2","load2","unloadS"};
-
-{string} Machines = inp_machines;
-{string} States = Machines union {"areaA","areaS"};
-
-int Index[s in States] = ord(States, s);
-
-tuple jobRecord {
-    string machine1;
-    int    durations1;
-    string machine2;
-    int    durations2;
-}
-jobRecord job[Jobs] = [j: <m1,ftoi(d1),m2,ftoi(d2)> | <j,m1,d1,m2,d2> in inp_jobs];
-
-{string} inputParameterNames = {k | <k,v> in inp_parameters};
-float parameters[inputParameterNames] = [k:v | <k,v> in inp_parameters];
-int loadDuration = ftoi(parameters["Load Duration"]);
-/* ------------------------ end data initialization section ------------------------ */
-
-/* ------------------------ begin core mathematics section ------------------------- */
-dvar interval act[Jobs][Tasks];
-
-stateFunction trolleyPosition;
-
-minimize max(j in Jobs) endOf(act[j]["unloadS"]);
-subject to {
-   // durations
-   forall(j in Jobs) {
-     lengthOf(act[j]["loadA"])    == loadDuration;
-     lengthOf(act[j]["unload1"])  == loadDuration;
-     lengthOf(act[j]["process1"]) == job[j].durations1;
-     lengthOf(act[j]["load1"])    == loadDuration;
-     lengthOf(act[j]["unload2"])  == loadDuration;
-     lengthOf(act[j]["process2"]) == job[j].durations2;
-     lengthOf(act[j]["load2"])    == loadDuration;
-     lengthOf(act[j]["unloadS"])  == loadDuration;
-   };
-
-   // precedence
-   forall(j in Jobs)
-        forall(ordered t1, t2 in Tasks)
-          endBeforeStart(act[j][t1], act[j][t2]);
-
-   // no-overlap on machines
-   forall (m in Machines) {
-     noOverlap( append(
-               all(j in Jobs: job[j].machine1==m) act[j]["process1"],
-               all(j in Jobs: job[j].machine2==m) act[j]["process2"])
-            );
-   }
-
-    // state constraints
-    forall(j in Jobs) {
-        alwaysEqual(trolleyPosition, act[j]["loadA"],   Index["areaA"]);
-        alwaysEqual(trolleyPosition, act[j]["unload1"], Index[job[j].machine1]);
-        alwaysEqual(trolleyPosition, act[j]["load1"],   Index[job[j].machine1]);
-        alwaysEqual(trolleyPosition, act[j]["unload2"], Index[job[j].machine2]);
-        alwaysEqual(trolleyPosition, act[j]["load2"],   Index[job[j].machine2]);
-        alwaysEqual(trolleyPosition, act[j]["unloadS"], Index["areaS"]);
-    };
- };
-
-/* ------------------------ end core mathematics section --------------------------- */
-
-
-/* ------------------------ begin ticdat output section ---------------------------- */
-include "ticdat_jobs_output.mod";
-
-execute {
-
-  for (var j in Jobs){
-     for (var t in Tasks){
-        sln_act.add(j,t,act[j][t]['start'], act[j][t]['end']);
-     }
-  }
-
-  writeOutputToFile();
-}
-/* ------------------------ end ticdat output section ------------------------------ */
\ No newline at end of file
diff --git a/examples/expert_section/opl/cp_jobs/jobs.py b/examples/expert_section/opl/cp_jobs/jobs.py
deleted file mode 100644
index 179fa0c2..00000000
--- a/examples/expert_section/opl/cp_jobs/jobs.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Constraint programming example based on https://ibm.co/2rGVyet
-
-# Implement core functionality needed to achieve modularity.
-# 1. Define the input data schema
-# 2. Define the output data schema
-# 3. Create a solve function that accepts a data set consistent with the input
-#    schema and (if possible) returns a data set consistent with the output schema.
-#
-# Provides command line interface via ticdat.standard_main
-# For example, typing
-#   python jobs.py -i input_data.xlsx -o solution_data.xlsx
-# will read from a model stored in the file input_data.xlsx and write the solution
-# to solution_data.xlsx.
-#
-# Note that file requires jobs.mod to be in the same directory
-
-from ticdat import TicDatFactory, standard_main, opl_run
-import time
-import datetime
-
-# ------------------------ define the input schema --------------------------------
-# There are three input tables, with 4 primary key fields and 4 data fields.
-input_schema = TicDatFactory (
-    parameters = [["Parameter"],["Value"]],
-    machines = [["Name"],[]],
-    jobs = [["Name"],["Machine1","Durations1","Machine2","Durations2"]]
-    )
-
-input_schema.set_data_type("parameters", "Parameter", number_allowed=False, strings_allowed=("Load Duration",))
-input_schema.set_data_type("parameters", "Value", min=0, max=float("inf"), inclusive_max=False)
-
-input_schema.add_foreign_key("jobs", "machines", ["Machine1", "Name"])
-input_schema.add_foreign_key("jobs", "machines", ["Machine2", "Name"])
-
-input_schema.set_data_type("jobs", "Durations1",  min=0, max=float("inf"), inclusive_max=False, must_be_int=True)
-input_schema.set_data_type("jobs", "Durations2",  min=0, max=float("inf"), inclusive_max=False, must_be_int=True)
-
-input_schema.set_data_type("jobs", "Machine1", number_allowed=False, strings_allowed='*')
-input_schema.set_data_type("jobs", "Machine2", number_allowed=False, strings_allowed='*')
-
-input_schema.opl_prepend = "inp_" # avoid table name collisions
-# ---------------------------------------------------------------------------------
-
-
-# ------------------------ define the output schema -------------------------------
-solution_schema = TicDatFactory(
-    act = [["Job", "Task"],["Start", "End"]],
-    act_as_dates = [["Job", "Task"],["Start Date", "End Date"]])
-
-solution_schema.opl_prepend = "sln_"
-# ---------------------------------------------------------------------------------
-
-
-# ------------------------ create a solve function --------------------------------
-def solve(dat):
-    """
-    core solving routine
-    :param dat: a good ticdat for the input_schema
-    :return: a good ticdat for the solution_schema, or None
-    """
-    assert input_schema.good_tic_dat_object(dat)
-    assert not input_schema.find_foreign_key_failures(dat)
-    assert not input_schema.find_data_type_failures(dat)
-    assert not input_schema.find_data_row_failures(dat)
-
-    rtn = opl_run("jobs.mod", input_schema, dat, solution_schema)
-    if rtn:
-        # for simplicities sake, I'm just going to treat OPL time as 'minutes from now'
-        now = time.time()
-        format_iso = lambda x : datetime.datetime.utcfromtimestamp(now + x*60).isoformat() + "Z"
-        for k,r in rtn.act.items():
-            rtn.act_as_dates[k] = [format_iso(r["Start"]), format_iso(r["End"])]
-        return rtn
-# ---------------------------------------------------------------------------------
-
-# ------------------------ provide stand-alone functionality ----------------------
-# when run from the command line, will read/write json/xls/csv/db/sql/mdb files
-if __name__ == "__main__":
-    standard_main(input_schema, solution_schema, solve)
-# ---------------------------------------------------------------------------------
diff --git a/examples/expert_section/opl/cp_jobs/jobs_sample_data.sql b/examples/expert_section/opl/cp_jobs/jobs_sample_data.sql
deleted file mode 100644
index 5ee322c8..00000000
--- a/examples/expert_section/opl/cp_jobs/jobs_sample_data.sql
+++ /dev/null
@@ -1,10 +0,0 @@
-INSERT INTO [jobs] ([Name],[Machine1],[Durations1],[Machine2],[Durations2]) VALUES ('j4','m1',160,'m3',100);
-INSERT INTO [jobs] ([Name],[Machine1],[Durations1],[Machine2],[Durations2]) VALUES ('j5','m3',180,'m2',80);
-INSERT INTO [jobs] ([Name],[Machine1],[Durations1],[Machine2],[Durations2]) VALUES ('j6','m2',140,'m3',60);
-INSERT INTO [jobs] ([Name],[Machine1],[Durations1],[Machine2],[Durations2]) VALUES ('j1','m1',80,'m2',60);
-INSERT INTO [jobs] ([Name],[Machine1],[Durations1],[Machine2],[Durations2]) VALUES ('j2','m2',120,'m3',80);
-INSERT INTO [jobs] ([Name],[Machine1],[Durations1],[Machine2],[Durations2]) VALUES ('j3','m2',80,'m1',60);
-INSERT INTO [parameters] ([Parameter],[Value]) VALUES ('Load Duration',20);
-INSERT INTO [machines] ([Name]) VALUES ('m1');
-INSERT INTO [machines] ([Name]) VALUES ('m3');
-INSERT INTO [machines] ([Name]) VALUES ('m2');
diff --git a/examples/expert_section/opl/diet/create_xls_file.py b/examples/expert_section/opl/diet/create_xls_file.py
deleted file mode 100644
index 663b62a0..00000000
--- a/examples/expert_section/opl/diet/create_xls_file.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from diet import input_schema
-dat = input_schema.sql.create_tic_dat_from_sql("diet_sample_data.sql")
-# writing out an .xls file instead of an .xlsx file because the .xlsx writer
-# doesn't handle infinty seamlessly
-input_schema.xls.write_file(dat, "Diet_Sample_Data.xls", allow_overwrite=True)
\ No newline at end of file
diff --git a/examples/expert_section/opl/diet/diet.mod b/examples/expert_section/opl/diet/diet.mod
deleted file mode 100644
index b37367ca..00000000
--- a/examples/expert_section/opl/diet/diet.mod
+++ /dev/null
@@ -1,39 +0,0 @@
-/*********************************************
- * OPL 12.6.0.0 Model
- * Author: Joshua
- * Creation Date: Jan 19, 2017 at 6:14:30 PM
- *********************************************/
-include "ticdat_diet.mod";
-
-{string} foodItems = {f | <f,c> in foods};
-float foodCost[foodItems] = [f: c | <f,c> in foods];
-
-dvar float+ purchase[foodItems];
-
-minimize
-  sum(f in foodItems) foodCost[f] * purchase[f];
-
-subject to {
-
-  forall (<category,min_nutrition,max_nutrition> in categories){
-    sum(<f,category,quantity> in nutrition_quantities) quantity * purchase[f] >= min_nutrition;
-    sum(<f,category,quantity> in nutrition_quantities) quantity * purchase[f] <= max_nutrition;
-  }
-
-}
-include "ticdat_diet_output.mod";
-float nutrition_consumed[c in categories] = sum(nq in nutrition_quantities: nq.category == c.name) nq.quantity * purchase[nq.food];
-
-float total_cost = sum(f in foodItems) foodCost[f] * purchase[f];
-execute {
-   for (var f in foodItems){
-      buy_food.add(f,purchase[f]);
-   }
-
-   for (var c in categories){
-      consume_nutrition.add(c.name,nutrition_consumed[c]);
-   }
-
-   parameters.add("Total Cost",total_cost);
-   writeOutputToFile();
-}
\ No newline at end of file
diff --git a/examples/expert_section/opl/diet/diet.py b/examples/expert_section/opl/diet/diet.py
deleted file mode 100644
index 35c6c60e..00000000
--- a/examples/expert_section/opl/diet/diet.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Implement core functionality needed to achieve modularity.
-# 1. Define the input data schema
-# 2. Define the output data schema
-# 3. Create a solve function that accepts a data set consistent with the input
-#    schema and (if possible) returns a data set consistent with the output schema.
-#
-# Provides command line interface via ticdat.standard_main
-# For example, typing
-#   python diet.py -i input_data.xlsx -o solution_data.xlsx
-# will read from a model stored in the file input_data.xlsx and write the solution
-# to solution_data.xlsx.
-#
-# Note that file requires diet.mod to be in the same directory
-
-from ticdat import TicDatFactory, standard_main, opl_run
-
-# ------------------------ define the input schema --------------------------------
-# There are three input tables, with 4 primary key fields and 4 data fields.
-input_schema = TicDatFactory (
-    categories = [["Name"],["Min Nutrition", "Max Nutrition"]],
-    foods  = [["Name"],["Cost"]],
-    nutrition_quantities = [["Food", "Category"], ["Quantity"]])
-
-# Define the data types
-input_schema.set_data_type("categories", "Min Nutrition", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=False)
-input_schema.set_data_type("categories", "Max Nutrition", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=True)
-input_schema.set_data_type("foods", "Cost", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=False)
-input_schema.set_data_type("nutrition_quantities", "Quantity", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=False)
-
-# We also want to insure that Max Nutrition doesn't fall below Min Nutrition
-input_schema.add_data_row_predicate(
-    "categories", predicate_name="Min Max Check",
-    predicate=lambda row : row["Max Nutrition"] >= row["Min Nutrition"])
-
-# The default-default of zero makes sense everywhere except for Max Nutrition
-input_schema.set_default_value("categories", "Max Nutrition", float("inf"))
-# ---------------------------------------------------------------------------------
-
-
-# ------------------------ define the output schema -------------------------------
-# There are three solution tables, with 3 primary key fields and 3 data fields.
-solution_schema = TicDatFactory(
-    parameters = [["Parameter"],["Value"]],
-    buy_food = [["Food"],["Quantity"]],
-    consume_nutrition = [["Category"],["Quantity"]])
-
-# ---------------------------------------------------------------------------------
-
-
-# ------------------------ create a solve function --------------------------------
-def solve(dat):
-    """
-    core solving routine
-    :param dat: a good ticdat for the input_schema
-    :return: a good ticdat for the solution_schema, or None
-    """
-    assert input_schema.good_tic_dat_object(dat)
-    assert not input_schema.find_foreign_key_failures(dat)
-    assert not input_schema.find_data_type_failures(dat)
-    assert not input_schema.find_data_row_failures(dat)
-
-    return opl_run("diet.mod", input_schema, dat, solution_schema)
-# ---------------------------------------------------------------------------------
-
-# ------------------------ provide stand-alone functionality ----------------------
-# when run from the command line, will read/write json/xls/csv/db/sql/mdb files
-if __name__ == "__main__":
-    standard_main(input_schema, solution_schema, solve)
-# ---------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/examples/expert_section/opl/diet/diet_sample_data.sql b/examples/expert_section/opl/diet/diet_sample_data.sql
deleted file mode 100644
index 7119f010..00000000
--- a/examples/expert_section/opl/diet/diet_sample_data.sql
+++ /dev/null
@@ -1,49 +0,0 @@
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('ice cream','protein',8);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('ice cream','fat',10);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('fries','sodium',270);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('fries','calories',380);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('hamburger','fat',26);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('macaroni','sodium',930);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('hot dog','sodium',1800);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('chicken','sodium',1190);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('salad','calories',320);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('ice cream','calories',330);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('milk','sodium',125);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('salad','sodium',1230);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('pizza','sodium',820);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('pizza','protein',15);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('pizza','calories',320);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('hamburger','calories',410);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('milk','fat',2.5);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('salad','protein',31);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('milk','protein',8);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('macaroni','fat',10);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('salad','fat',12);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('hot dog','fat',32);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('chicken','fat',10);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('chicken','protein',32);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('fries','protein',4);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('pizza','fat',12);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('milk','calories',100);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('ice cream','sodium',180);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('chicken','calories',420);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('hamburger','sodium',730);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('macaroni','calories',320);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('fries','fat',19);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('hot dog','calories',560);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('hot dog','protein',20);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('macaroni','protein',12);
-INSERT INTO [nutrition_quantities] ([food],[category],[Quantity]) VALUES ('hamburger','protein',24);
-INSERT INTO [foods] ([name],[cost]) VALUES ('hamburger',2.49);
-INSERT INTO [foods] ([name],[cost]) VALUES ('salad',2.49);
-INSERT INTO [foods] ([name],[cost]) VALUES ('hot dog',1.5);
-INSERT INTO [foods] ([name],[cost]) VALUES ('fries',1.89);
-INSERT INTO [foods] ([name],[cost]) VALUES ('macaroni',2.09);
-INSERT INTO [foods] ([name],[cost]) VALUES ('chicken',2.89);
-INSERT INTO [foods] ([name],[cost]) VALUES ('milk',0.89);
-INSERT INTO [foods] ([name],[cost]) VALUES ('ice cream',1.59);
-INSERT INTO [foods] ([name],[cost]) VALUES ('pizza',1.99);
-INSERT INTO [categories] ([name],[min nutrition],[max nutrition]) VALUES ('protein',91,'inf');
-INSERT INTO [categories] ([name],[min nutrition],[max nutrition]) VALUES ('calories',1800,2200);
-INSERT INTO [categories] ([name],[min nutrition],[max nutrition]) VALUES ('fat',0,65);
-INSERT INTO [categories] ([name],[min nutrition],[max nutrition]) VALUES ('sodium',0,1779);
diff --git a/examples/expert_section/opl/netflow/create_xlsx_file.py b/examples/expert_section/opl/netflow/create_xlsx_file.py
deleted file mode 100644
index dc3a3408..00000000
--- a/examples/expert_section/opl/netflow/create_xlsx_file.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from netflow import input_schema
-dat = input_schema.sql.create_tic_dat_from_sql("netflow_sample_data.sql")
-input_schema.xls.write_file(dat, "Netflow_Sample_Data.xlsx", allow_overwrite=True)
\ No newline at end of file
diff --git a/examples/expert_section/opl/netflow/netflow.mod b/examples/expert_section/opl/netflow/netflow.mod
deleted file mode 100644
index ee543d7f..00000000
--- a/examples/expert_section/opl/netflow/netflow.mod
+++ /dev/null
@@ -1,48 +0,0 @@
-/*********************************************
- * OPL 12.6.0.0 Model
- * Author: Joshua
- * Creation Date: Jan 19, 2017 at 9:04:01 PM
- *********************************************/
-include "ticdat_netflow.mod";
-
-tuple edgeType {
-	string start;
-	string end;
-}
-
-{edgeType} edges = {<s,e> | <s,e,c> in arcs};
-float edgeCapacity[edges] = [<s,e> : c | <s,e,c> in arcs];
-
-dvar float+ edgeFlow[e in edges][commodities] in 0..edgeCapacity[e];
-
-minimize
-  sum(<com,start,end,flowCost> in cost) flowCost * edgeFlow[<start,end>][com];
-  
-subject to {
-
-  //for each inflow, ensure the flow demands are met
-  forall (<com,node,qty> in inflow)
-    - sum(<start,node> in edges) edgeFlow[<start,node>][com] 
-    + sum(<node,end> in edges) edgeFlow[<node,end>][com] == qty;
-    
-  //for each edge, ensure the flow of all commodities is less than or equal to the capacity
-  forall (<start,end> in edges)
-    sum(com in commodities) edgeFlow[<start,end>][com] <= edgeCapacity[<start,end>];
-
-}
-include "ticdat_netflow_output.mod";
-
-//flow = {resultType} results = {<start,end,com,edgeFlow[<start,end>][com]> |
-//  <start,end> in edges,com in commodities};
-
-float total_cost = sum(<com,start,end,flowCost> in cost) flowCost * edgeFlow[<start,end>][com];
-execute {
-   for (var c in commodities){
-      for (var e in edges) {
-         flow.add(c,e.start,e.end,edgeFlow[e][c]);
-      }
-   }
-
-   parameters.add("Total Cost",total_cost);
-   writeOutputToFile();
-}
\ No newline at end of file
diff --git a/examples/expert_section/opl/netflow/netflow.py b/examples/expert_section/opl/netflow/netflow.py
deleted file mode 100644
index 75908a4c..00000000
--- a/examples/expert_section/opl/netflow/netflow.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Solve a multi-commodity flow problem as python package.
-
-# Implement core functionality needed to achieve modularity.
-# 1. Define the input data schema
-# 2. Define the output data schema
-# 3. Create a solve function that accepts a data set consistent with the input
-#    schema and (if possible) returns a data set consistent with the output schema.
-#
-# Provides command line interface via ticdat.standard_main
-# For example, typing
-#   python netflow.py -i csv_data -o solution_csv_data
-# will read from a model stored in .csv files in the csv_data directory
-# and write the solution to .csv files in the solution_csv_data directory
-#
-# Note that file requires netflow.mod to be in the same directory
-
-from ticdat import TicDatFactory, standard_main, opl_run
-
-# ------------------------ define the input schema --------------------------------
-input_schema = TicDatFactory(
-    commodities=[["Name"], []],
-    nodes=[["Name"], []],
-    arcs=[["Source", "Destination"], ["Capacity"]],
-    cost=[["Commodity", "Source", "Destination"], ["Cost"]],
-    inflow=[["Commodity", "Node"], ["Quantity"]]
-)
-
-# add foreign key constraints
-input_schema.add_foreign_key("arcs", "nodes", ['Source', 'Name'])
-input_schema.add_foreign_key("arcs", "nodes", ['Destination', 'Name'])
-input_schema.add_foreign_key("cost", "nodes", ['Source', 'Name'])
-input_schema.add_foreign_key("cost", "nodes", ['Destination', 'Name'])
-input_schema.add_foreign_key("cost", "commodities", ['Commodity', 'Name'])
-input_schema.add_foreign_key("inflow", "commodities", ['Commodity', 'Name'])
-input_schema.add_foreign_key("inflow", "nodes", ['Node', 'Name'])
-
-input_schema.set_data_type("arcs", "Capacity",  max=float("inf"),
-                           inclusive_max=True)
-input_schema.set_data_type("cost", "Cost")
-input_schema.set_data_type("inflow", "Quantity", min=-float("inf"),
-                          inclusive_min=False)
-# ---------------------------------------------------------------------------------
-
-# ------------------------ define the output schema -------------------------------
-solution_schema = TicDatFactory(
-    flow=[["Commodity", "Source", "Destination"], ["Quantity"]],
-    parameters=[["Parameter"], ["Value"]])
-# ---------------------------------------------------------------------------------
-
-# ------------------------ solving section-----------------------------------------
-def solve(dat):
-    """
-    core solving routine
-    :param dat: a good ticdat for the input_schema
-    :return: a good ticdat for the solution_schema, or None
-    """
-    return opl_run("netflow.mod", input_schema, dat, solution_schema)
-
-# ---------------------------------------------------------------------------------
-
-# ------------------------ provide stand-alone functionality ----------------------
-# when run from the command line, will read/write json/xls/csv/db/sql/mdb files
-if __name__ == "__main__":
-    standard_main(input_schema, solution_schema, solve)
-# ---------------------------------------------------------------------------------
diff --git a/examples/expert_section/opl/netflow/netflow_sample_data.sql b/examples/expert_section/opl/netflow/netflow_sample_data.sql
deleted file mode 100644
index ce61eebc..00000000
--- a/examples/expert_section/opl/netflow/netflow_sample_data.sql
+++ /dev/null
@@ -1,35 +0,0 @@
-INSERT INTO [commodities] ([name]) VALUES ('Pencils');
-INSERT INTO [commodities] ([name]) VALUES ('Pens');
-INSERT INTO [nodes] ([name]) VALUES ('Seattle');
-INSERT INTO [nodes] ([name]) VALUES ('Boston');
-INSERT INTO [nodes] ([name]) VALUES ('New York');
-INSERT INTO [nodes] ([name]) VALUES ('Denver');
-INSERT INTO [nodes] ([name]) VALUES ('Detroit');
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pencils','Denver','Seattle',30.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pens','Denver','Seattle',30.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pencils','Detroit','New York',20.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pens','Detroit','New York',20.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pens','Detroit','Boston',20.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pencils','Detroit','Boston',10.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pens','Detroit','Seattle',80.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pens','Denver','New York',70.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pens','Denver','Boston',60.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pencils','Denver','New York',40.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pencils','Detroit','Seattle',60.0);
-INSERT INTO [cost] ([commodity],[source],[destination],[cost]) VALUES ('Pencils','Denver','Boston',40.0);
-INSERT INTO [arcs] ([source],[destination],[capacity]) VALUES ('Detroit','Seattle',120.0);
-INSERT INTO [arcs] ([source],[destination],[capacity]) VALUES ('Denver','Seattle',120.0);
-INSERT INTO [arcs] ([source],[destination],[capacity]) VALUES ('Detroit','New York',80.0);
-INSERT INTO [arcs] ([source],[destination],[capacity]) VALUES ('Denver','New York',120.0);
-INSERT INTO [arcs] ([source],[destination],[capacity]) VALUES ('Denver','Boston',120.0);
-INSERT INTO [arcs] ([source],[destination],[capacity]) VALUES ('Detroit','Boston',100.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pens','Boston',-40.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pens','New York',-30.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pencils','Detroit',50.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pencils','Seattle',-10.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pencils','Denver',60.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pens','Detroit',60.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pens','Seattle',-30.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pens','Denver',40.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pencils','New York',-50.0);
-INSERT INTO [inflow] ([commodity],[node],[quantity]) VALUES ('Pencils','Boston',-50.0);
diff --git a/examples/expert_section/opl/oil_blend/create_xlsx_file.py b/examples/expert_section/opl/oil_blend/create_xlsx_file.py
deleted file mode 100644
index f308f505..00000000
--- a/examples/expert_section/opl/oil_blend/create_xlsx_file.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from oil_blend import input_schema
-dat = input_schema.json.create_tic_dat("oil_blend_sample_data.json")
-input_schema.xls.write_file(dat, "Oil_Blend_Sample_Data.xlsx", allow_overwrite=True)
\ No newline at end of file
diff --git a/examples/expert_section/opl/oil_blend/oil_blend.mod b/examples/expert_section/opl/oil_blend/oil_blend.mod
deleted file mode 100644
index 01d29e35..00000000
--- a/examples/expert_section/opl/oil_blend/oil_blend.mod
+++ /dev/null
@@ -1,102 +0,0 @@
-/*********************************************
- * OPL oil blending problem organized into tabular, ticdat compliant format
- * See https://goo.gl/kqXmQE for reference problem and sample data.
- *********************************************/
-
-/* ------------------------ begin data initialization section ---------------------- */
-/* creates inp_oil, inp_gas, inp_parameters */
-include "ticdat_oil_blend.mod";
-
-/* use the inp_ data to populate the original data structures */
-
-{string} Gasolines = {n | <n,d,s,o,l> in inp_gas};
-{string} Oils = {n | <n,s,p,o,l> in inp_oil};
-tuple gasType {
-  float demand;
-  float price;
-  float octane;
-  float lead;
-}
-
-tuple oilType {
-  float capacity;
-  float price;
-  float octane;
-  float lead;
-}
-
-gasType Gas[Gasolines] = [n: <d,s,o,l> |  <n,d,s,o,l> in inp_gas];
-oilType Oil[Oils] = [n: <s,p,o,l> | <n,s,p,o,l> in inp_oil];
-
-{string} inputParameterNames = {k | <k,v> in inp_parameters};
-float parameters[inputParameterNames] = [k:v | <k,v> in inp_parameters];
-float MaxProduction = parameters["Maximum Production"];
-float ProdCost =  parameters["Production Cost"];
-/* ------------------------ end data initialization section ------------------------ */
-
-
-/* ------------------------ begin core mathematics section ------------------------- */
-/* This section is copied unaltered from oil.mod file at https://goo.gl/kqXmQE       */
-
-dvar float+ a[Gasolines];
-dvar float+ Blend[Oils][Gasolines];
-
-
-maximize
-  sum( g in Gasolines , o in Oils )
-    (Gas[g].price - Oil[o].price - ProdCost) * Blend[o][g]
-    - sum(g in Gasolines) a[g];
-subject to {
-  forall( g in Gasolines )
-    ctDemand:
-      sum( o in Oils )
-        Blend[o][g] == Gas[g].demand + 10*a[g];
-  forall( o in Oils )
-    ctCapacity:
-      sum( g in Gasolines )
-        Blend[o][g] <= Oil[o].capacity;
-  ctMaxProd:
-    sum( o in Oils , g in Gasolines )
-      Blend[o][g] <= MaxProduction;
-  forall( g in Gasolines )
-    ctOctane:
-      sum( o in Oils )
-        (Oil[o].octane - Gas[g].octane) * Blend[o][g] >= 0;
-  forall( g in Gasolines )
-    ctLead:
-      sum( o in Oils )
-        (Oil[o].lead - Gas[g].lead) * Blend[o][g] <= 0;
-}
-
-execute DISPLAY_REDUCED_COSTS{
-  for( var g in Gasolines ) {
-    writeln("a[",g,"].reducedCost = ",a[g].reducedCost);
-  }
-}
-/* ------------------------ end core mathematics section --------------------------- */
-
-
-/* ------------------------ begin ticdat output section ---------------------------- */
-/* write out solution */
-include "ticdat_oil_blend_output.mod";
-float total_advertising = sum(g in Gasolines) a[g];
-float total_purchase_cost = sum( g in Gasolines , o in Oils ) Oil[o].price * Blend[o][g];
-float total_production_cost = sum( g in Gasolines , o in Oils ) ProdCost * Blend[o][g];
-float total_revenue = sum( g in Gasolines , o in Oils ) Gas[g].price * Blend[o][g];
-execute {
-   for (var o in Oils){
-        for (var g in Gasolines){
-            sln_blending.add(o,g,Blend[o][g]);
-        }
-   }
-   for (var g in Gasolines){
-        sln_advertising.add(g,a[g]);
-   }
-   sln_parameters.add("Total Advertising Spend",total_advertising);
-   sln_parameters.add("Total Oil Purchase Cost",total_purchase_cost);
-   sln_parameters.add("Total Production Cost",total_production_cost);
-   sln_parameters.add("Total Revenue",total_revenue);
-   sln_parameters.add("Total Profit",total_revenue - total_advertising - total_purchase_cost - total_production_cost);
-   writeOutputToFile();
-}
-/* ------------------------ end ticdat output section ------------------------------ */
diff --git a/examples/expert_section/opl/oil_blend/oil_blend.py b/examples/expert_section/opl/oil_blend/oil_blend.py
deleted file mode 100644
index 9f281cc8..00000000
--- a/examples/expert_section/opl/oil_blend/oil_blend.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Solves the oil blending problem defined at https://goo.gl/kqXmQE
-
-# Implement core functionality needed to achieve modularity.
-# 1. Define the input data schema
-# 2. Define the output data schema
-# 3. Create a solve function that accepts a data set consistent with the input
-#    schema and (if possible) returns a data set consistent with the output schema.
-#
-# Provides command line interface via ticdat.standard_main
-# For example, typing
-#   python oil_blend.py -i input_data.xlsx -o solution_data.xlsx
-# will read from a model stored in the file input_data.xlsx and write the solution
-# to solution_data.xlsx.
-
-# Note that this file requires oil_blend.mod to be in the same directory
-
-from ticdat import TicDatFactory, standard_main, opl_run
-
-# ------------------------ define the input schema --------------------------------
-input_schema = TicDatFactory (
-     parameters = [["Parameter"],["Value"]],
-     gas = [["Name"],["Demand","Sales Price","Min Octane Rating", "Max Lead Contents"]],
-     oil = [["Name"],["Supply","Purchase Price","Octane Rating", "Lead Contents"]])
-
-# no foreign keys
-
-input_schema.set_data_type("parameters", "Parameter", number_allowed = False,
-                           strings_allowed= ["Maximum Production", "Production Cost"])
-input_schema.set_data_type("parameters", "Value", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=True)
-input_schema.set_data_type("gas", "Demand", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=False)
-input_schema.set_data_type("gas", "Sales Price", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=False)
-input_schema.set_data_type("gas", "Min Octane Rating", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=False)
-input_schema.set_data_type("gas", "Max Lead Contents", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=True)
-input_schema.set_data_type("oil", "Supply", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=True)
-input_schema.set_data_type("oil", "Purchase Price", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=False)
-input_schema.set_data_type("oil", "Octane Rating", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=False)
-input_schema.set_data_type("oil", "Lead Contents", min=0, max=float("inf"),
-                           inclusive_min=True, inclusive_max=False)
-# We tolerate float("inf") as the Maximum Production but not the Production Cost
-input_schema.add_data_row_predicate("parameters",
-                                    lambda row : not (row["Parameter"] == "Production Cost" and
-                                                      row["Value"] == float("inf")))
-
-input_schema.opl_prepend = "inp_" # avoid table name collisions
-# ---------------------------------------------------------------------------------
-
-
-# ------------------------ define the output schema -------------------------------
-solution_schema = TicDatFactory(
-    parameters = [["Parameter"],["Value"]],
-    advertising = [["Gas"],["Dollars Spent"]],
-    blending = [["Oil","Gas"],["Quantity"]])
-
-solution_schema.opl_prepend = "sln_" # avoid table name collisions
-# ---------------------------------------------------------------------------------
-
-# ------------------------ create a solve function --------------------------------
-def solve(dat):
-    """
-    core solving routine
-    :param dat: a good ticdat for the input_schema
-    :return: a good ticdat for the solution_schema, or None
-    """
-    assert input_schema.good_tic_dat_object(dat)
-    assert not input_schema.find_foreign_key_failures(dat)
-    assert not input_schema.find_data_type_failures(dat)
-    assert not input_schema.find_data_row_failures(dat)
-
-    return opl_run("oil_blend.mod", input_schema, dat, solution_schema)
-# ---------------------------------------------------------------------------------
-
-# ------------------------ provide stand-alone functionality ----------------------
-# when run from the command line, will read/write json/xls/csv/db/sql/mdb files
-if __name__ == "__main__":
-    standard_main(input_schema, solution_schema, solve)
-# ---------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/examples/expert_section/opl/oil_blend/oil_blend_sample_data.json b/examples/expert_section/opl/oil_blend/oil_blend_sample_data.json
deleted file mode 100644
index a666779d..00000000
--- a/examples/expert_section/opl/oil_blend/oil_blend_sample_data.json
+++ /dev/null
@@ -1,58 +0,0 @@
-{
-  "gas": [
-    {
-      "Demand": 3000, 
-      "Max Lead Contents": 1, 
-      "Min Octane Rating": 10, 
-      "Name": "Super", 
-      "Sales Price": 70
-    }, 
-    {
-      "Demand": 2000, 
-      "Max Lead Contents": 2, 
-      "Min Octane Rating": 8, 
-      "Name": "Regular", 
-      "Sales Price": 60
-    }, 
-    {
-      "Demand": 1000, 
-      "Max Lead Contents": 1, 
-      "Min Octane Rating": 6, 
-      "Name": "Diesel", 
-      "Sales Price": 50
-    }
-  ], 
-  "oil": [
-    {
-      "Lead Contents": 0.5, 
-      "Name": "Crude1", 
-      "Octane Rating": 12, 
-      "Purchase Price": 45, 
-      "Supply": 5000
-    }, 
-    {
-      "Lead Contents": 3, 
-      "Name": "Crude3", 
-      "Octane Rating": 8, 
-      "Purchase Price": 25, 
-      "Supply": 5000
-    }, 
-    {
-      "Lead Contents": 2, 
-      "Name": "Crude2", 
-      "Octane Rating": 6, 
-      "Purchase Price": 35, 
-      "Supply": 5000
-    }
-  ], 
-  "parameters": [
-    {
-      "Parameter": "Maximum Production",
-      "Value": 14000
-    }, 
-    {
-      "Parameter": "Production Cost",
-      "Value": 4
-    }
-  ]
-}
\ No newline at end of file
diff --git a/examples/expert_section/opl/oil_blend/readme.txt b/examples/expert_section/opl/oil_blend/readme.txt
deleted file mode 100644
index dcc01631..00000000
--- a/examples/expert_section/opl/oil_blend/readme.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-An expected use of the ticdat.opl_run functionality would be to add Python connectivity to
-pre-existing OPL projects. In order to streamline this process, its important to simplify the
-extent to which the current .mod file needs to be edited in order to communicate with ticdat.
-The TicDatFactory.opl_prepend attribute anticipates this concern by giving you the ability
-to prepend a short string onto the names of the data structures created by the auto-generated
-.mod files. This allows you to define the input and solution schemas with whatever table names
-make the most sense, without concern that such names will collide with variable and tuple names
-in your current .mod file.
-
-As a result, the core mathematical logic of your .mod files can be preserved completely. The
-porting process only requires you to edit the data initialization section at the beginning of
-your .mod file, and add a simple "write to ticdat" section at the end of your .mod file.
-(Of course, a ticdat-based Python configuration file is also required).
-
-To demonstrate, please consider the OPL oil blending example at https://goo.gl/kqXmQE. This
-example contains an oil.mod file that solves the oil blending problem, and an oil.dat file
-that defines a sample data set. We used this oil.dat as the starting point for the ticdat
-compatible oil_blend.mod file presented here. Specifically, oil_blend.mod contains three sections.
-
- * A data initialization section. This was created via a simple editing process of the first 22
-   lines of oil.mod. In oil_blend.mod, Gasolines, Oils, Gas, Oil, MaxProduction and ProdCost are
-   all populated from the the relevant data provided by inp_gas, inp_oil, and inp_parameters. When
-   comparing to oil.mod, you can see that just the six lines that use ...; were altered, and the
-   inputParameterNames, parameters data structures were added. The rest of the code in this
-   section is exactly the same as in oil.mod.
-
- * A core mathematical section. This section of oil_blend.mod performs the actual optimization. It
-   is copied over unaltered from oil.mod.
-
- * A ticdat output section. This section of the code was added to populate the sln_ data
-   structures created for the solution schema. It computes a series of KPIs to be stored in the
-   solution parameters table, and then populates all three solution tables in an execute block.
-
-As you can see, even though the input schema contains tables named gas and oil, the resulting
-auto-generated code populates data structures named inp_gas and inp_oil. This avoids naming
-collisions with the gas and oil variables defined in oil.mod, and precludes any need to rename 
-these variables in oil_blend.mod. Similarly, the parameters tables for the input and solution 
-schema correspond to inp_parameters and sln_parameters, and thus it is easy to distinguish between 
-them in oil_blend.mod.
diff --git a/examples/expert_section/opl/readme b/examples/expert_section/opl/readme
deleted file mode 100644
index d92f1550..00000000
--- a/examples/expert_section/opl/readme
+++ /dev/null
@@ -1,47 +0,0 @@
-Please refer to video https://www.youtube.com/watch?v=RCtglvt8exI&t=9s.
-
-Once Python is installed, install the latest version of ticdat using the command 
-   'pip install ticdat'
-If you already have ticdat, but want to upgrade to the most recent version
-   'pip install ticdat —upgrade'
-
-After that, use the command line to navigate to the directory containing the oplrun executable. 
-
-In this video that directory is C:\Program Files\IBM\CPLEX_Studio_Community127\opl\bin\x64_win64
-
-Then from this directory, run the oplrun_setup.py file found at http://bit.ly/2sEOMUe
-
-Once OPL is connected to Ticdat, navigate to a directory containing the OPL .mod file, 
-the associated Python file, and the dataset. This video uses Ticdat's Oil blend example 
-http://bit.ly/2rqoWWi.
-
-The oil_blend.mod file has 3 extra lines to communicate with ticdat
-     `include "ticdat_oil_blend.mod";`   
-        (This includes an file autogenerated by Ticdat which defines the input schema)
-     `include "ticdat_oil_blend_output.mod";` 
-        (This includes an file autogenerated by Ticdat which defines the output schema)
-     `writeOutputToFile();` 
-        (This line tells OPL to output the solution in a format able to be read by Ticdat)
-
-The Python file is a light wrapper which defines the input and output schemas, as well as 
-providing the option to check data integrity before it gets to OPL.
-
-To run the file, navigate to the folder containing the model using the command line.
-Then enter the command `python <python-file> -i <input-data-to-be-used> -o <file-to-output-solution>`
-The input and output files can be one of a number of formats, including JSON, SQL, and XLSX 
-(you can also import one format and output in another).
-
-Running the OPL model through Ticdat generates a number of intermediate files. 
-For the oil-blend example, these are:
-   "oil-solution.xlsx" - The solution output in .xlsx format, 
-                         which is what was specified when the oil-blend.py was run
-   "output.txt" - The console output from running oplrun, this is useful when debugging problems
-   "results.dat" - This is the solution output formatted as an OPL .dat file
-   "temp.dat" - This is the input data formatted as an OPL .dat file
-   "ticdat_oil_blend.mod" - This is a OPL .mod file which defines the input schema
-   "ticdat_oil_blend_output.mod" - This is a OPL .mod file which defines the output schema.
-
-These files let you run the OPL model directly through OPL, without using Ticdat. 
-For the Oil example this would be done by entering the following from the same directory 
-as above:
-   `oplrun oil-blend.mod temp.dat`
diff --git a/oplrun_setup.py b/oplrun_setup.py
deleted file mode 100644
index f4ff7e37..00000000
--- a/oplrun_setup.py
+++ /dev/null
@@ -1,8 +0,0 @@
-print "This script configures a oplrun_path.txt file in order to enable use with oplrun."
-print "You need to run this script from the directory containing oplrun "
-print "(or oplrun.exe on windows)"
-print "This is a one-time post 'pip install' setup step."
-
-import ticdat.testing.ticdattestutils as tdu
-tdu.configure_oplrun_path()
-
diff --git a/setup.py b/setup.py
index 69953420..84408b78 100644
--- a/setup.py
+++ b/setup.py
@@ -13,7 +13,7 @@
 	license = 'BSD 2-Clause',
 	author = 'Pete Cacioppi',
 	author_email= 'peter.cacioppi@gmail.com',
-	maintainer_email = '12samn@gmail.com',
+	maintainer_email = '12samn@gmail.com, Kelley.Shannon.patricia@gmail.com',
 	url = 'https://github.com/ticdat/ticdat/',
 	classifiers = [
 		"Development Status :: 5 - Production/Stable",
diff --git a/ticdat/__init__.py b/ticdat/__init__.py
index 1728d5f5..f1a1a3a1 100644
--- a/ticdat/__init__.py
+++ b/ticdat/__init__.py
@@ -11,10 +11,8 @@
 from ticdat.ticdatfactory import TicDatFactory, freeze_me
 from ticdat.utils import Sloc, LogFile, Progress, Slicer, standard_main, \
                          gurobi_env, ampl_format, verify, faster_df_apply
-from ticdat.opl import opl_run, create_opl_mod_text, create_opl_mod_output_text
 from ticdat.model import Model
 from ticdat.pandatfactory import PanDatFactory
-__version__ = '0.2.24.6'
+__version__ = '0.2.25'
 __all__ = ["TicDatFactory", "PanDatFactory", "freeze_me", "LogFile", "Sloc", "Slicer", "Progress", "standard_main",
-           "Model", "opl_run", "create_opl_mod_text", "create_opl_mod_output_text", "ampl_format",
-           "gurobi_env", "verify", "faster_df_apply"]
+           "Model", "ampl_format", "gurobi_env", "verify", "faster_df_apply"]
diff --git a/ticdat/opl.py b/ticdat/opl.py
deleted file mode 100644
index 5d3a6bcd..00000000
--- a/ticdat/opl.py
+++ /dev/null
@@ -1,352 +0,0 @@
-from ticdat.utils import verify, containerish, stringish, find_duplicates_from_dict_ticdat
-from ticdat.utils import find_case_space_duplicates, change_fields_with_reserved_keywords
-import ticdat.utils as tu
-from ticdat.ticdatfactory import TicDatFactory
-import os, subprocess, inspect, time, uuid, shutil
-from collections import defaultdict
-from ticdat.jsontd import make_json_dict
-
-INFINITY = 999999
-
-opl_keywords = ["initial", "template", "struct", "all", "and", "assert", "boolean", "constraints", "CP",
-                "CPLEX", "cumulFunction", "DBConnection", "DBExecute", "DBRead", "DBUpdate", "dexpr",
-                "diff", "div", "dvar", "else", "execute", "false", "float", "float+", "forall", "from",
-                "in", "if", "include", "infinity", "int", "int+", "intensity", "inter", "interval",
-                "invoke", "key", "main", "max", "maximize", "maxint", "min", "minimize", "mod", "not",
-                "optional", "or", "ordered", "piecewise", "prepare", "prod", "pwlFunction", "range",
-                "reversed", "sequence", "setof", "SheetConnection", "SheetRead", "SheetWrite", "size",
-                "sorted", "SPSSConnection", "SPSSRead", "stateFunction", "stepFunction", "stepwise",
-                "string", "subject", "sum", "symdiff", "to", "true", "tuple", "types", "union", "using", "with"]
-
-def _code_dir():
-    return os.path.dirname(os.path.abspath(inspect.getsourcefile(_code_dir)))
-
-def _fix_fields_with_opl_keywords(tdf):
-    return change_fields_with_reserved_keywords(tdf, opl_keywords)
-
-def _unfix_fields_with_opl_keywords(tdf):
-    return change_fields_with_reserved_keywords(tdf, opl_keywords, True)
-
-def opl_run(mod_file, input_tdf, input_dat, soln_tdf, infinity=INFINITY, oplrun_path=None):
-    """
-    solve an optimization problem using an OPL .mod file
-    :param mod_file: An OPL .mod file.
-    :param input_tdf: A TicDatFactory defining the input schema
-    :param input_dat: A TicDat object consistent with input_tdf
-    :param soln_tdf: A TicDatFactory defining the solution schema
-    :param infinity: A number used to represent infinity in OPL
-    :return: a TicDat object consistent with soln_tdf, or None if no solution found
-    """
-    verify(os.path.isfile(mod_file), "mod_file %s is not a valid file."%mod_file)
-    verify(not find_case_space_duplicates(input_tdf), "There are case space duplicate field names in the input schema.")
-    verify(not find_case_space_duplicates(soln_tdf), "There are case space duplicate field names in the solution schema.")
-    verify(len({input_tdf.opl_prepend + t for t in input_tdf.all_tables}.union(
-               {soln_tdf.opl_prepend + t for t in soln_tdf.all_tables})) ==
-           len(input_tdf.all_tables) + len(soln_tdf.all_tables),
-           "There are colliding input and solution table names.\nSet opl_prepend so " +
-           "as to insure the input and solution table names are effectively distinct.")
-    msg  = []
-    verify(input_tdf.good_tic_dat_object(input_dat, msg.append),
-           "tic_dat not a good object for the input_tdf factory : %s"%"\n".join(msg))
-    orig_input_tdf, orig_soln_tdf = input_tdf, soln_tdf
-    input_tdf = _fix_fields_with_opl_keywords(input_tdf)
-    soln_tdf = _fix_fields_with_opl_keywords(soln_tdf)
-    input_dat = input_tdf.TicDat(**make_json_dict(orig_input_tdf, input_dat))
-    assert input_tdf.good_tic_dat_object(input_dat)
-    mod_file_name = os.path.basename(mod_file)[:-4]
-    with open(mod_file, "r") as f:
-        mod = f.read()
-        assert 'writeOutputToFile()' in mod
-        assert ("ticdat_" + mod_file_name + ".mod") in mod
-        assert ("ticdat_" + mod_file_name + "_output.mod") in mod
-    working_dir = os.path.abspath(os.path.dirname(mod_file))
-    if tu.development_deployed_environment:
-        working_dir = os.path.join(working_dir, "oplticdat_%s"%uuid.uuid4())
-        shutil.rmtree(working_dir, ignore_errors = True)
-        os.mkdir(working_dir)
-        working_dir = os.path.abspath(working_dir)
-        _ = os.path.join(working_dir, os.path.basename(mod_file))
-        shutil.copy(mod_file, _)
-        mod_file = _
-    datfile = os.path.join(working_dir, "temp.dat")
-    output_txt = os.path.join(working_dir, "output.txt")
-    results_dat = os.path.join(working_dir, "results.dat")
-    if os.path.isfile(results_dat):
-        os.remove(results_dat)
-    with open(datfile, "w") as f:
-        f.write(create_opl_text(input_tdf, input_dat, infinity))
-    verify(os.path.isfile(datfile), "Could not create temp.dat")
-    with open(os.path.join(working_dir, "ticdat_"+mod_file_name+".mod"), "w") as f:
-        f.write("/* Autogenerated input file, created by opl.py on " + time.asctime() + " */\n")
-        f.write(create_opl_mod_text(orig_input_tdf))
-    with open(os.path.join(working_dir,"ticdat_"+mod_file_name+"_output.mod"), "w") as f:
-        f.write("/* Autogenerated output file, created by opl.py on " + time.asctime() + " */\n")
-        f.write(create_opl_mod_output_text(orig_soln_tdf))
-    if not oplrun_path:
-        verify_str = "need to either pass oplrun_path argument or run oplrun_setup.py"
-        if tu.development_deployed_environment:
-            verify_str = "could not find oplrun. Make sure the Application Type is set correctly"
-        verify(os.path.isfile(os.path.join(_code_dir(),"oplrun_path.txt")), verify_str)
-        with open(os.path.join(_code_dir(),"oplrun_path.txt"),"r") as f:
-            oplrun_path = f.read().strip()
-    verify(os.path.isfile(oplrun_path), "%s not a valid path to oplrun"%oplrun_path)
-    if tu.development_deployed_environment:
-        if "LD_LIBRARY_PATH" not in os.environ.keys():
-            os.environ["LD_LIBRARY_PATH"] = os.path.abspath(os.path.join(oplrun_path,'..'))
-        elif not oplrun_path in os.environ["LD_LIBRARY_PATH"]:
-            os.environ["LD_LIBRARY_PATH"] = os.path.abspath(os.path.join(oplrun_path,'..')) + \
-                                            ":" + os.environ["LD_LIBRARY_PATH"]
-    try:
-        output = subprocess.check_output([oplrun_path, mod_file, datfile], stderr=subprocess.STDOUT)
-    except subprocess.CalledProcessError as err:
-        if tu.development_deployed_environment:
-            raise Exception("oplrun failed to complete: " + err.output)
-        output = err.output
-    with open(output_txt, "w") as f:
-        f.write(str(output))
-    if not os.path.isfile(results_dat):
-        print("%s is not a valid file. A solution was likely not generated. Check 'output.txt' for details."%results_dat)
-        return None
-    with open(results_dat, "r") as f:
-        output = f.read()
-    soln_tdf = _unfix_fields_with_opl_keywords(soln_tdf)
-    return read_opl_text(soln_tdf, output, False)
-
-_can_run_oplrun_tests = os.path.isfile(os.path.join(_code_dir(),"oplrun_path.txt"))
-
-def create_opl_text(tdf, tic_dat, infinity=INFINITY):
-    """
-    Generate a OPL .dat string from a TicDat object
-    :param tdf: A TicDatFactory defining the schema
-    :param tic_dat: A TicDat object consistent with tdf
-    :param infinity: A number used to represent infinity in OPL
-    :return: A string consistent with the OPL .dat format
-    """
-    msg = []
-    verify(tdf.good_tic_dat_object(tic_dat, msg.append),
-           "tic_dat not a good object for this factory : %s"%"\n".join(msg))
-    verify(not tdf.generator_tables, "doesn't work with generator tables.")
-    verify(not tdf.generic_tables, "doesn't work with generic tables. (not yet - will add ASAP as needed) ")
-    dict_with_lists = defaultdict(list)
-    dict_tables = {t for t,pk in tdf.primary_key_fields.items() if pk}
-    for t in dict_tables:
-        for k,r in getattr(tic_dat, t).items():
-            row = list(k) if containerish(k) else [k]
-            for f in tdf.data_fields.get(t, []):
-                row.append(r[f])
-            dict_with_lists[t].append(row)
-    for t in set(tdf.all_tables).difference(dict_tables):
-        for r in getattr(tic_dat, t):
-            row = [r[f] for f in tdf.data_fields[t]]
-            dict_with_lists[t].append(row)
-
-    rtn = ""
-    for i, (t,l) in enumerate(dict_with_lists.items()):
-        rtn += "\n" if i > 0 else ""
-        rtn += "%s = {"%(tdf.opl_prepend + t)
-        if len(l[0]) > 1:
-            rtn += "\n"
-        for x in range(len(l)):
-            r = l[x]
-            if len(r) > 1:
-                rtn += "<"
-            for i,v in enumerate(r):
-                rtn += ('"%s"'%v if stringish(v) else (str(infinity) if float('inf') == v else str(v))) + (", " if i < len(r)-1 else "")
-            if len(r) == 1 and len(l)-1 != x:
-                rtn += ', '
-            if len(r) > 1:
-                rtn += ">\n"
-        rtn += "};\n"
-
-    return rtn
-
-def create_opl_mod_text(tdf):
-    """
-    Generate a OPL .mod string from a TicDat object for diagnostic purposes
-    :param tdf: A TicDatFactory defining the input schema
-    :return: A string consistent with the OPL .mod input format
-    """
-    return _create_opl_mod_text(tdf, False)
-
-def create_opl_mod_output_text(tdf):
-    """
-    Generate a OPL .mod string from a TicDat object for diagnostic purposes
-    :param tdf: A TicDatFactory defining the input schema
-    :return: A string consistent with the OPL .mod output format
-    """
-    return _create_opl_mod_text(tdf, True)
-
-def _create_opl_mod_text(tdf, output):
-    verify(not find_case_space_duplicates(tdf), "There are case space duplicate field names in the schema.")
-    verify(not tdf.generator_tables, "Input schema error - doesn't work with generator tables.")
-    verify(not tdf.generic_tables, "Input schema error - doesn't work with generic tables. (not yet - will \
-            add ASAP as needed) ")
-    tdf = _fix_fields_with_opl_keywords(tdf)
-    rtn = ''
-    dict_tables = {t for t, pk in tdf.primary_key_fields.items() if pk}
-    verify(set(dict_tables) == set(tdf.all_tables), "not yet handling non-PK tables of any sort")
-
-    prepend = getattr(tdf, "opl_prepend", "")
-    def _get_type(data_types, table, field, is_pk=False):
-        try:
-            return "float" if data_types[table][field].number_allowed else "string"
-        except KeyError:
-            if is_pk:
-                return "string"
-            return "float"
-
-    def get_table_as_mod_text(tdf, tbn, output):
-        rtn = ''
-        sig = '{}' if output else '...'
-        if len(tdf.primary_key_fields[tbn]) == 1 and len(tdf.data_fields[tbn]) == 0:
-            rtn = "{" + _get_type(tdf.data_types, tbn, tdf.primary_key_fields[tbn][0], True) + "} " + \
-                  prepend + tbn + " = " + sig + ";\n\n"
-        else:
-            rtn += "tuple " + prepend + tbn + "_type\n{"
-            for pk in tdf.primary_key_fields[tbn]:
-                pk_m = pk.replace(' ', '_').lower()
-                rtn += "\n\tkey " + _get_type(tdf.data_types, tbn, pk, True) + " " + pk_m + ";"
-            for df in tdf.data_fields[tbn]:
-                df_m = df.replace(' ', '_').lower()
-                rtn += "\n\t" + _get_type(tdf.data_types, tbn, df) + " " + df_m + ";"
-            rtn += "\n};\n\n{" + prepend + tbn + "_type} " + prepend + tbn + "=" + sig + ";\n\n"
-        return rtn
-
-    for t in dict_tables:
-        rtn += get_table_as_mod_text(tdf, t, output)
-
-    if output:
-        # Add a function writeOutputToFile() to the mod file
-        rtn += '\nexecute {\n\tfunction writeOutputToFile() {'
-        rtn += '\n\t\tvar ofile = new IloOplOutputFile("results.dat");'
-        for t in dict_tables:
-            rtn += '\n\tofile.writeln("' + prepend + t + ' = ", ' + prepend + t + ');'
-        rtn+='\n\t}\t\n}'
-    return rtn
-
-def read_opl_text(tdf,text, commaseperator = True):
-    """
-    Read an OPL .dat string
-    :param tdf: A TicDatFactory defining the schema
-    :param text: A string consistent with the OPL .dat format
-    :return: A TicDat object consistent with tdf
-    """
-    verify(stringish(text), "text needs to be a string")
-    # probably want to verify something about the ticdat factory, look at the wiki
-    dict_with_lists = defaultdict(list)
-    NONE, TABLE, ROW, ROWSTRING, ROWNUM, FIELD, STRING,  NUMBER = 1, 2, 3, 4, 5, 6, 7, 8
-    mode = NONE
-    field = ''
-    table_name = ''
-    row = []
-
-    def to_number(st, pos):
-        try:
-            return float(st)
-        except ValueError:
-            verify(False,
-                   "Badly formatted string - Field '%s' is not a valid number. Character position [%s]." % (st, pos))
-
-    for i,c in enumerate(text):
-        if mode not in [STRING, ROWSTRING] and (c.isspace() or c == '{' or c == ';'):
-            if mode in [NUMBER, ROWNUM, FIELD] and not commaseperator:
-                c = ','
-            else:
-                continue
-        if mode in [STRING, ROWSTRING]:
-            if c == '"':
-                if text[i-1] == '\\':
-                    field = field[:-1] + '"'
-                else:
-                    if mode is ROWSTRING:
-                        row.append(field)
-                        field = ''
-                        verify(len(row) == len((dict_with_lists[table_name] or [row])[0]),
-                               "Inconsistent row lengths found for table %s" % table_name)
-                        dict_with_lists[table_name].append(row)
-                        row = []
-                        mode = TABLE
-                    else:
-                        mode = FIELD
-            else:
-                field += c
-        elif c == '=':
-            verify(mode is NONE, "Badly formatted string, unrecognized '='. Character position [%s]"%i)
-            verify(len(table_name) > 0, "Badly formatted string, table name can't be blank. Character position [%s]"%i)
-            verify(table_name not in dict_with_lists.keys(), "Can't have duplicate table name. [Character position [%s]"%i)
-            dict_with_lists[table_name] = []
-            mode = TABLE
-        elif c == '<':
-            verify(mode is TABLE, "Badly formatted string, unrecognized '<'. Character position [%s]"%i)
-            mode = ROW
-
-        elif c == ',':
-            verify(mode in [ROW, FIELD, NUMBER, ROWNUM, TABLE], "Badly formatted string, unrecognized ','. \
-                                                                    Character position [%s]"%i)
-            if mode is TABLE:
-                continue
-            if mode is ROWNUM:
-                field = to_number(field,i)
-                row.append(field)
-                field = ''
-                verify(len(row) == len((dict_with_lists[table_name] or [row])[0]),
-                       "Inconsistent row lengths found for table %s" % table_name)
-                dict_with_lists[table_name].append(row)
-                row = []
-                mode = TABLE
-            else:
-                if mode is NUMBER:
-                    field = to_number(field,i)
-                row.append(field)
-                field = ''
-                mode = ROW
-
-        elif c == '"':
-            verify(mode in [ROW, TABLE], "Badly formatted string, unrecognized '\"'. Character position [%s]"%i)
-            if mode is ROW:
-                mode = STRING
-            if mode is TABLE:
-                mode = ROWSTRING
-
-        elif c == '}':
-            verify(mode in [TABLE, ROWNUM], "Badly formatted string, unrecognized '}'. Character position [%s]"%i)
-            if mode is ROWNUM:
-                field = to_number(field,i)
-                row.append(field)
-                field = ''
-                verify(len(row) == len((dict_with_lists[table_name] or [row])[0]),
-                       "Inconsistent row lengths found for table %s" % table_name)
-                dict_with_lists[table_name].append(row)
-            row = []
-            table_name = ''
-            mode = NONE
-
-        elif c == '>':
-            verify(mode in [ROW, FIELD, NUMBER], "Badly formatted string, unrecognized '>'. \
-                                                                    Character position [%s]"%i)
-            if mode is NUMBER:
-                field = to_number(field,i)
-                mode = FIELD
-            if mode is FIELD:
-                row.append(field)
-                field = ''
-            verify(len(row) == len((dict_with_lists[table_name] or [row])[0]),
-                   "Inconsistent row lengths found for table %s"%table_name)
-            dict_with_lists[table_name].append(row)
-            row = []
-            mode = TABLE
-        else:
-            verify(mode in [NONE, ROW, ROWNUM, FIELD, NUMBER], "Badly formatted string, \
-                                                                    unrecognized '%s'. Character position [%s]"%(c,i))
-            if mode is NONE:
-                table_name += c
-            elif mode is TABLE:
-                mode = ROWNUM
-                field += c
-            else:
-                mode = NUMBER
-                field += c
-    assert not find_duplicates_from_dict_ticdat(tdf, dict_with_lists), \
-            "duplicates were found - if asserts are disabled, duplicate rows will overwrite"
-
-    return tdf.TicDat(**{k.replace(tdf.opl_prepend,"",1):v for k,v in dict_with_lists.items()})
diff --git a/ticdat/ticdatfactory.py b/ticdat/ticdatfactory.py
index 8d87c8b3..bb0a417d 100644
--- a/ticdat/ticdatfactory.py
+++ b/ticdat/ticdatfactory.py
@@ -35,7 +35,7 @@ def _keylen(k) :
         rtn = 0
     return rtn
 
-class TicDatFactory(freezable_factory(object, "_isFrozen", {"opl_prepend", "ampl_prepend"})) :
+class TicDatFactory(freezable_factory(object, "_isFrozen", {"ampl_prepend"})) :
     """
     Primary class for ticdat library. This class is constructed with a schema.
     It can be used to generate TicDat objects, to write TicDat objects to
@@ -2193,19 +2193,10 @@ def fix_all_row(all_row):
         assert len(rtn.renamings) == len(reverse_renamings)
         return rtn
 
-    @property
-    def opl_prepend(self):
-        return self._prepends.get("opl", "")
-
     @property
     def ampl_prepend(self):
         return self._prepends.get("ampl","")
 
-    @opl_prepend.setter
-    def opl_prepend(self, value):
-        verify(utils.stringish(value), "opl_prepend should be a string")
-        self._prepends["opl"] = value
-
     @ampl_prepend.setter
     def ampl_prepend(self,value):
         verify(utils.stringish(value), "ampl_prepend should be a string")
diff --git a/ticdat/utils.py b/ticdat/utils.py
index ff015da5..5cee8108 100644
--- a/ticdat/utils.py
+++ b/ticdat/utils.py
@@ -824,8 +824,6 @@ def change_fields_with_reserved_keywords(tdf, reserved_keywords, undo=False):
         if original_field in tdf.data_types.get(table, ()):
             rtn.set_data_type(table, new_field,
                               *(tdf.data_types[table][original_field]))
-    if hasattr(tdf,'opl_prepend'):
-        rtn.opl_prepend = tdf.opl_prepend
     if hasattr(tdf,'ampl_prepend'):
         rtn.ampl_prepend = tdf.ampl_prepend
     return rtn


Test patch:
diff --git a/ticdat/testing/testopl.py b/ticdat/testing/testopl.py
deleted file mode 100644
index 9ebeda34..00000000
--- a/ticdat/testing/testopl.py
+++ /dev/null
@@ -1,228 +0,0 @@
-import os
-from ticdat.opl import create_opl_text, read_opl_text, opl_run,create_opl_mod_text
-from ticdat.opl import _can_run_oplrun_tests
-import sys
-from ticdat.ticdatfactory import TicDatFactory
-import ticdat.utils as utils
-from ticdat.testing.ticdattestutils import dietData, dietSchema, netflowData, addNetflowDataTypes, nearlySame
-from ticdat.testing.ticdattestutils import  netflowSchema,sillyMeData, sillyMeSchema
-from ticdat.testing.ticdattestutils import fail_to_debugger, flagged_as_run_alone, get_testing_file_path
-import unittest
-
-#@fail_to_debugger
-class TestOpl(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-        cls._original_value = utils.development_deployed_environment
-        utils.development_deployed_environment = True
-    @classmethod
-    def tearDownClass(cls):
-        utils.development_deployed_environment = cls._original_value
-    def testDiet_oplrunRequired(self):
-        self.assertTrue(_can_run_oplrun_tests)
-        diet_schema = {"categories" : (("Name",),["Min Nutrition", "Max Nutrition"]),
-                       "foods" :[["Name"],("Cost",)],
-                       "nutritionQuantities" : (["Food", "Category"], ["Qty"])
-                      }
-        in_tdf = TicDatFactory(**diet_schema)
-        soln_tdf = TicDatFactory(
-            parameters=[["Parameter Name"], ["Parameter Value"]],
-            buy_food=[["Food"], ["Qty"]],consume_nutrition=[["Category"], ["Qty"]])
-        makeDat = lambda : in_tdf.TicDat(
-            categories = {'calories': [1800, 2200],
-                          'protein':  [91,   float("inf")],
-                          'fat':      [0, 65],
-                          'sodium':   [0, 1779]},
-
-            foods = {'hamburger': 2.49,
-                      'chicken':   2.89,
-                      'hot dog':   1.50,
-                      'fries':     1.89,
-                      'macaroni':  2.09,
-                      'pizza':     1.99,
-                      'salad':     2.49,
-                      'milk':      0.89,
-                      'ice cream': 1.59},
-            nutritionQuantities= [('hamburger', 'calories', 410),
-                                  ('hamburger', 'protein', 24),
-                                  ('hamburger', 'fat', 26),
-                                  ('hamburger', 'sodium', 730),
-                                  ('chicken',   'calories', 420),
-                                  ('chicken',   'protein', 32),
-                                  ('chicken',   'fat', 10),
-                                  ('chicken',   'sodium', 1190),
-                                  ('hot dog',   'calories', 560),
-                                  ('hot dog',   'protein', 20),
-                                  ('hot dog',   'fat', 32),
-                                  ('hot dog',   'sodium', 1800),
-                                  ('fries',     'calories', 380),
-                                  ('fries',     'protein', 4),
-                                  ('fries',     'fat', 19),
-                                  ('fries',     'sodium', 270),
-                                  ('macaroni',  'calories', 320),
-                                  ('macaroni',  'protein', 12),
-                                  ('macaroni',  'fat', 10),
-                                  ('macaroni',  'sodium', 930),
-                                  ('pizza',     'calories', 320),
-                                  ('pizza',     'protein', 15),
-                                  ('pizza',     'fat', 12),
-                                  ('pizza',     'sodium', 820),
-                                  ('salad',     'calories', 320),
-                                  ('salad',     'protein', 31),
-                                  ('salad',     'fat', 12),
-                                  ('salad',     'sodium', 1230),
-                                  ('milk',      'calories', 100),
-                                  ('milk',      'protein', 8),
-                                  ('milk',      'fat', 2.5),
-                                  ('milk',      'sodium', 125),
-                                  ('ice cream', 'calories', 330),
-                                  ('ice cream', 'protein', 8),
-                                  ('ice cream', 'fat', 10),
-                                  ('ice cream', 'sodium', 180) ] )
-        opl_soln = opl_run(get_testing_file_path("sample_diet.mod"), in_tdf, makeDat(), soln_tdf)
-        self.assertTrue(nearlySame(opl_soln.parameters["Total Cost"]["Parameter Value"], 11.829, epsilon=0.0001))
-        self.assertTrue(nearlySame(opl_soln.consume_nutrition["protein"]["Qty"], 91, epsilon=0.0001))
-        # opl_run should not complete when there is an infeasible solution
-        dat = makeDat()
-        dat.categories["calories"]["Min Nutrition"] = dat.categories["calories"]["Max Nutrition"]+1
-        try:
-            opl_soln = opl_run(get_testing_file_path("sample_diet.mod"), in_tdf, dat, soln_tdf)
-            self.assertTrue(False)
-        except:
-            self.assertTrue(True)
-    def testNetflow_oplrunRequired(self):
-        self.assertTrue(_can_run_oplrun_tests)
-        in_tdf = TicDatFactory(**netflowSchema())
-        in_tdf.enable_foreign_key_links()
-        soln_tdf = TicDatFactory(flow=[["source", "destination", "commodity"], ["quantity"]],
-                                 parameters=[["paramKey"], ["value"]])
-        dat = in_tdf.TicDat(**{t: getattr(netflowData(), t) for t in in_tdf.primary_key_fields})
-        opl_soln = opl_run(get_testing_file_path("sample_netflow.mod"), in_tdf, dat, soln_tdf)
-        self.assertTrue(nearlySame(opl_soln.parameters["Total Cost"]["value"],5500))
-        self.assertTrue(nearlySame(opl_soln.flow["Pens", "Detroit", "New York"]["quantity"], 30))
-    def testDiet(self):
-        tdf = TicDatFactory(**dietSchema())
-        tdf.enable_foreign_key_links()
-        oldDat = tdf.TicDat(**{t:getattr(dietData(),t) for t in tdf.primary_key_fields})
-        oldDatStr = create_opl_text(tdf, oldDat)
-        newDat = read_opl_text(tdf, oldDatStr)
-        self.assertFalse(tdf._same_data(oldDat, newDat))
-        oldDat.categories["protein"]["maxNutrition"]=12 # Remove infinity from the data
-        changedDatStr = create_opl_text(tdf, oldDat)
-        changedDat = read_opl_text(tdf, changedDatStr)
-        self.assertTrue(tdf._same_data(oldDat,changedDat))
-        tdf.opl_prepend = "pre_"
-        origStr, changedDatStr = changedDatStr, create_opl_text(tdf, oldDat)
-        changedDat = read_opl_text(tdf, changedDatStr)
-        self.assertTrue(tdf._same_data(oldDat,changedDat))
-        self.assertFalse(origStr == changedDatStr)
-
-    def testNetflow(self):
-        tdf = TicDatFactory(**netflowSchema())
-        tdf.enable_foreign_key_links()
-        oldDat = tdf.freeze_me(tdf.TicDat(**{t:getattr(netflowData(),t) for t in tdf.primary_key_fields}))
-        oldDatStr = create_opl_text(tdf, oldDat)
-        newDat = read_opl_text(tdf, oldDatStr)
-        self.assertTrue(tdf._same_data(oldDat, newDat))
-        tdf.opl_prepend = "stuff"
-        oldDatStr = create_opl_text(tdf, oldDat)
-        newDat = read_opl_text(tdf, oldDatStr)
-        self.assertTrue(tdf._same_data(oldDat, newDat))
-
-    def testSilly(self):
-        tdf = TicDatFactory(**sillyMeSchema())
-        tdf.enable_foreign_key_links()
-        oldDat = tdf.freeze_me(tdf.TicDat(**sillyMeData()))
-        oldDatStr = create_opl_text(tdf, oldDat)
-        newDat = read_opl_text(tdf, oldDatStr)
-        self.assertTrue(tdf._same_data(oldDat, newDat))
-        tdf.opl_prepend = "ooooo"
-        oldDatStr = create_opl_text(tdf, oldDat)
-        newDat = read_opl_text(tdf, oldDatStr)
-        self.assertTrue(tdf._same_data(oldDat, newDat))
-
-    def testOther(self):
-        tdf = TicDatFactory(
-            table1=[["String_Field"], []],
-            table2=[["String_Field", "Num_PK"], ["Num_Field_1","Num_Field_2"]])
-        data = {
-            "table1": {
-                "test1": [],
-                "test2": [],
-            },
-            "table2": {
-                ("test1",1): [2,3],
-                ("test2",2): [3,4]
-            }
-        }
-        oldDat = tdf.freeze_me(tdf.TicDat(**data))
-        oldDatStr = create_opl_text(tdf, oldDat)
-        newDat = read_opl_text(tdf, oldDatStr)
-        self.assertTrue(tdf._same_data(oldDat, newDat))
-        tdf.opl_prepend = "_"
-        oldDatStr = create_opl_text(tdf, oldDat)
-        newDat = read_opl_text(tdf, oldDatStr)
-        self.assertTrue(tdf._same_data(oldDat, newDat))
-
-    def testCreateModText(self):
-        tdf = TicDatFactory(
-            table1=[["string_pk", "num_pk"], ["num_field1","string_field2"]])
-        tdf.set_data_type("table1", "num_pk", min=0, max=float("inf"), inclusive_min=True, inclusive_max=False)
-        tdf.set_data_type("table1", "string_field2", number_allowed=False, strings_allowed='*')
-        modStr = create_opl_mod_text(tdf)
-        self.assertTrue("key string string_pk;" in modStr)
-        self.assertTrue("key float num_pk;" in modStr)
-        self.assertTrue("float num_field1;" in modStr)
-        self.assertTrue("string string_field2;" in modStr)
-
-    def testReadModText(self):
-        tdf1 = TicDatFactory( test_1 = [["sf1"],["sf2","nf1","nf2"]] )
-        tdf1.set_data_type("test_1", "sf2", number_allowed=False, strings_allowed='*')
-        test_str = 'test_1 =  {<"s1" "s2" 1 2> <"s3" "s4" 0 0>}'
-        test_dat = read_opl_text(tdf1,test_str,False)
-        self.assertTrue(test_dat.test_1["s1"]["sf2"] == "s2")
-        self.assertTrue(test_dat.test_1["s1"]["nf2"] == 2)
-        self.assertTrue(test_dat.test_1["s2"]["nf1"] == 0)
-
-        tdf2 = TicDatFactory( test_2 = [["sf1"],[]] )
-        test_str = 'test_2 =  {<"s3">}'
-        test_dat = read_opl_text(tdf2,test_str,False)
-        self.assertTrue(list(test_dat.test_2.keys())[0] == "s3")
-
-        tdf3 = TicDatFactory( test_3 = [["nf1"],[]] )
-        tdf3.set_data_type("test_3", "nf1", min=0, max=float("inf"), inclusive_min=True, inclusive_max=False)
-        test_str = 'test_3 =  {<6> <5>}'
-        test_dat = read_opl_text(tdf3,test_str,False)
-        self.assertTrue(6 in test_dat.test_3.keys())
-        self.assertTrue(5 in test_dat.test_3.keys())
-        self.assertTrue(len(test_dat.test_3.keys()) == 2)
-
-        tdf4 = TicDatFactory( test_4 = [["nf1"],["nf2","nf3","nf4"]] )
-        tdf4.set_data_type("test_4", "nf1", min=0, max=float("inf"), inclusive_min=True, inclusive_max=False)
-        tdf4.set_data_type("test_4", "nf2", min=0, max=float("inf"), inclusive_min=True, inclusive_max=False)
-        tdf4.set_data_type("test_4", "nf3", min=0, max=float("inf"), inclusive_min=True, inclusive_max=False)
-        tdf4.set_data_type("test_4", "nf4", min=0, max=float("inf"), inclusive_min=True, inclusive_max=False)
-        test_str = 'test_4 =  {<7 0 809 9>}'
-        test_dat = read_opl_text(tdf4,test_str,False)
-        self.assertTrue(7 in test_dat.test_4.keys())
-        self.assertTrue(len(test_dat.test_4[7]) == 3)
-        self.assertTrue(test_dat.test_4[7]["nf3"] == 809)
-
-        tdf5 = TicDatFactory( test_5 = [["sf1"],["sf2"]] )
-        tdf5.set_data_type("test_5", "sf2", number_allowed=False, strings_allowed='*')
-        test_str = 'test_5 =  {<"s4" "s5">}'
-        test_dat = read_opl_text(tdf5,test_str,False)
-        self.assertTrue("s4" in test_dat.test_5.keys())
-        self.assertTrue(test_dat.test_5["s4"]["sf2"] == "s5")
-
-        tdf6 = TicDatFactory( test_6 = [["nf1"],["sf1"]] )
-        tdf6.set_data_type("test_6", "nf1", min=0, max=float("inf"), inclusive_min=True, inclusive_max=False)
-        tdf6.set_data_type("test_6", "sf1", number_allowed=False, strings_allowed='*')
-        test_str = 'test_6 =  {<0 "s6">}'
-        test_dat = read_opl_text(tdf6,test_str,False)
-        self.assertTrue(0 in test_dat.test_6.keys())
-        self.assertTrue(test_dat.test_6[0.0]['sf1'] == "s6")
-
-
-if __name__ == "__main__":
-    unittest.main()
\ No newline at end of file
diff --git a/ticdat/testing/ticdattestutils.py b/ticdat/testing/ticdattestutils.py
index c9a4b039..ea891562 100644
--- a/ticdat/testing/ticdattestutils.py
+++ b/ticdat/testing/ticdattestutils.py
@@ -34,20 +34,6 @@ def configure_blank_accdb():
     verify(os.path.isfile(os.path.join(mdb_dir, "mdb.py")), "mdb.py is missing. %s"%v_str)
     copy("blank.accdb", mdb_dir)
 
-def configure_oplrun_path():
-    if am_on_windows:
-        oplrun_name = os.path.abspath('oplrun.exe')
-    else:
-        oplrun_name = os.path.abspath('oplrun')
-    verify(os.path.isfile(oplrun_name), "You need to be in the directory containing oplrun")
-    opl_dir = os.path.abspath(os.path.join(_codeDir(), ".."))
-    v_str = "Please submit an issue at https://github.com/ticdat/ticdat/issues"
-    verify(os.path.isdir(opl_dir), "%s is strangely not a directory. %s"%(opl_dir, v_str))
-    verify(os.path.isfile(os.path.join(opl_dir,"opl.py")), "opl.py is missing. %s"%v_str)
-    oplrun_path = os.path.abspath(oplrun_name)
-    with open(os.path.join(opl_dir, "oplrun_path.txt"), "w") as f:
-        f.write(oplrun_path)
-
 _debug = []
 def _asserting() :
     _debug.append(())