func NewLHTip(mfr, ttype string, minvol, maxvol float64, volunit string) *LHTip { var lht LHTip lht.ID = GetUUID() lht.Mnfr = mfr lht.Type = ttype v := wunit.NewVolume(maxvol, volunit) lht.MaxVol = &v v2 := wunit.NewVolume(minvol, volunit) lht.MinVol = &v2 return &lht }
func MinMinVol(channels []*wtype.LHChannelParameter) wunit.Volume { mmv := wunit.NewVolume(9999999.0, "ul") for _, c := range channels { if c.Minvol.LessThan(&mmv) { mmv = *(c.Minvol) } } return mmv }
func makeCyBio() *liquidhandling.LHProperties { tips := GetTipList() layout := make(map[string]wtype.Coordinates) for i := 0; i < 12; i++ { posname := fmt.Sprintf("position_%d", i+1) // dont know coords for this yet var crds wtype.Coordinates layout[posname] = crds } lhp := liquidhandling.NewLHProperties(12, "Felix", "CyBio", "discrete", "disposable", layout) for _, tt := range tips { tb := GetTipByType(tt) if tb.Mnfr == lhp.Mnfr { lhp.Tips = append(lhp.Tips, tb.Tips[0][0]) } } lhp.Tip_preferences = []int{1, 5, 3} lhp.Input_preferences = []int{10, 11, 12} lhp.Output_preferences = []int{7, 8, 9, 2, 4} minvol := wunit.NewVolume(10, "ul") maxvol := wunit.NewVolume(1000, "ul") minspd := wunit.NewFlowRate(0.5, "ml/min") maxspd := wunit.NewFlowRate(2, "ml/min") hvconfig := wtype.NewLHChannelParameter("HVconfig", &minvol, &maxvol, &minspd, &maxspd, 8, false, wtype.LHVChannel, 0) hvadaptor := wtype.NewLHAdaptor("HVAdaptor", "CyBio", hvconfig) minvol = wunit.NewVolume(0.5, "ul") maxvol = wunit.NewVolume(50, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") lvconfig := wtype.NewLHChannelParameter("LVconfig", &minvol, &maxvol, &minspd, &maxspd, 8, false, wtype.LHVChannel, 0) lvadaptor := wtype.NewLHAdaptor("LVAdaptor", "CyBio", lvconfig) minvol = wunit.NewVolume(0.5, "ul") maxvol = wunit.NewVolume(1000, "ul") headparams := wtype.NewLHChannelParameter("ChoiceHead", &minvol, &maxvol, &minspd, &maxspd, 8, false, wtype.LHVChannel, 0) head := wtype.NewLHHead("ChoiceHead", "CyBio", headparams) head.Adaptor = hvadaptor lhp.Adaptors = append(lhp.Adaptors, hvadaptor) lhp.Adaptors = append(lhp.Adaptors, lvadaptor) lhp.Heads = append(lhp.Heads, head) lhp.HeadsLoaded = append(lhp.HeadsLoaded, head) return lhp }
func TestIPLinear(*testing.T) { // get component library ctypes := factory.GetComponentList() // make components cmps := make(map[string]wunit.Volume) for _, cmpn := range ctypes { vf := rand.Float64() * 10000.0 vol := wunit.NewVolume(vf, "ul") cmps[cmpn] = vol } // get plate library plist := factory.GetPlateList() // no need to subselect just stick em all in plates := make([]*wtype.LHPlate, 0) for _, p := range plist { plates = append(plates, factory.GetPlateByType(p)) } // we need a map between components and volumes // an array of plates // and a map of weights and constraints wtc := make(map[string]float64, 3) wtc["MAX_N_PLATES"] = 2.0 wtc["MAX_N_WELLS"] = 96.0 wtc["RESIDUAL_VOLUME_WEIGHT"] = 1.0 ass := choose_plate_assignments(cmps, plates, wtc) ass = ass /* for component, cmap := range ass { for plt, nw := range cmap { volreq := cmps[component] fmt.Println("\t", nw, " wells of ", plt.Type, " total volume ", float64(nw)*(plt.Welltype.Vol-plt.Welltype.Rvol), " residual volume ", float64(nw)*plt.Welltype.Rvol, " volume required: ", volreq.RawValue()) } } */ }
func makeManual() *liquidhandling.LHProperties { // tips := GetTipList() // dummy layout of 25 positions... arbitrary limitation x := 0.0 y := 0.0 z := 0.0 xinc := 100.0 yinc := 100.0 i := 0 layout := make(map[string]wtype.Coordinates) for xi := 0; xi < 5; xi++ { for yi := 0; yi < 5; yi++ { posname := fmt.Sprintf("position_%d", i+1) crds := wtype.Coordinates{x, y, z} layout[posname] = crds i += 1 y += yinc } x += xinc } lhp := liquidhandling.NewLHProperties(25, "Human", "MotherNature", "discrete", "disposable", layout) SetUpTipsFor(lhp) lhp.Tip_preferences = []string{"tips1", "tips2", "tips3", "tips4"} lhp.Input_preferences = []string{"in1", "in2", "in3", "in4"} lhp.Output_preferences = []string{"out1", "out2", "out3", "out4"} lhp.Tipwaste_preferences = []string{"tip_waste"} lhp.Wash_preferences = []string{"tip_wash"} lhp.Waste_preferences = []string{"liquid_waste"} adaptors := make([]*wtype.LHAdaptor, 0, 1) minvol := wunit.NewVolume(200, "ul") maxvol := wunit.NewVolume(1000, "ul") minspd := wunit.NewFlowRate(0.5, "ml/min") maxspd := wunit.NewFlowRate(2, "ml/min") hvconfig := wtype.NewLHChannelParameter("P1000Config", "Gilson", minvol, maxvol, minspd, maxspd, 1, false, wtype.LHVChannel, 0) hvadaptor := wtype.NewLHAdaptor("P1000", "Gilson", hvconfig) adaptors = append(adaptors, hvadaptor) minvol = wunit.NewVolume(50, "ul") maxvol = wunit.NewVolume(200, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") mvconfig := wtype.NewLHChannelParameter("P200Config", "Gilson", minvol, maxvol, minspd, maxspd, 1, false, wtype.LHVChannel, 0) mvadaptor := wtype.NewLHAdaptor("P200", "Gilson", mvconfig) adaptors = append(adaptors, mvadaptor) minvol = wunit.NewVolume(2, "ul") maxvol = wunit.NewVolume(20, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") lmvconfig := wtype.NewLHChannelParameter("P20Config", "Gilson", minvol, maxvol, minspd, maxspd, 1, false, wtype.LHVChannel, 0) lmvadaptor := wtype.NewLHAdaptor("P20", "Gilson", lmvconfig) adaptors = append(adaptors, lmvadaptor) minvol = wunit.NewVolume(1, "ul") maxvol = wunit.NewVolume(10, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") lvconfig := wtype.NewLHChannelParameter("P10Config", "Gilson", minvol, maxvol, minspd, maxspd, 1, false, wtype.LHVChannel, 0) lvadaptor := wtype.NewLHAdaptor("P10", "Gilson", lvconfig) adaptors = append(adaptors, lvadaptor) minvol = wunit.NewVolume(0.2, "ul") maxvol = wunit.NewVolume(2, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") vlvconfig := wtype.NewLHChannelParameter("P2Config", "Gilson", minvol, maxvol, minspd, maxspd, 1, false, wtype.LHVChannel, 0) vlvadaptor := wtype.NewLHAdaptor("P2", "Gilson", vlvconfig) adaptors = append(adaptors, vlvadaptor) //minvol = wunit.NewVolume(0.2, "ul") //maxvol = wunit.NewVolume(5000, "ul") for i, adaptor := range adaptors { maxvol = adaptor.Params.Maxvol minvol = adaptor.Params.Minvol maxspd = adaptor.Params.Maxspd minspd = adaptor.Params.Minspd headparams := wtype.NewLHChannelParameter(fmt.Sprintf("LabHand_%d", i+1), "MotherNature", minvol, maxvol, minspd, maxspd, 8, false, wtype.LHVChannel, 0) head := wtype.NewLHHead(fmt.Sprintf("LabHand_%d", i+1), "MotherNature", headparams) head.Adaptor = adaptor lhp.Heads = append(lhp.Heads, head) lhp.HeadsLoaded = append(lhp.HeadsLoaded, head) } // lhp.Adaptors = append(lhp.Adaptors, hvadaptor) // lhp.Adaptors = append(lhp.Adaptors, mvadaptor) // lhp.Adaptors = append(lhp.Adaptors, lmvadaptor) // lhp.Adaptors = append(lhp.Adaptors, lvadaptor) // lhp.Adaptors = append(lhp.Adaptors, vlvadaptor) return lhp }
func makeManual() *liquidhandling.LHProperties { // tips := GetTipList() // dummy layout of 25 positions... arbitrary limitation x := 0.0 y := 0.0 z := 0.0 xinc := 100.0 yinc := 100.0 i := 0 layout := make(map[string]wtype.Coordinates) for xi := 0; xi < 5; xi++ { for yi := 0; yi < 5; yi++ { posname := fmt.Sprintf("position_%d", i+1) crds := wtype.Coordinates{x, y, z} layout[posname] = crds i += 1 y += yinc } x += xinc } lhp := liquidhandling.NewLHProperties(25, "Human", "MotherNature", "discrete", "disposable", layout) lhp.Tip_preferences = []int{2, 3, 4, 5, 6} lhp.Input_preferences = []int{7, 8, 9, 10, 11, 12, 13, 14, 15} lhp.Output_preferences = []int{16, 17, 18, 19, 20, 21, 22, 23, 24, 25} minvol := wunit.NewVolume(200, "ul") maxvol := wunit.NewVolume(1000, "ul") minspd := wunit.NewFlowRate(0.5, "ml/min") maxspd := wunit.NewFlowRate(2, "ml/min") hvconfig := wtype.NewLHChannelParameter("P1000Config", &minvol, &maxvol, &minspd, &maxspd, 1, false, wtype.LHVChannel, 0) hvadaptor := wtype.NewLHAdaptor("P1000", "Gilson", hvconfig) minvol = wunit.NewVolume(50, "ul") maxvol = wunit.NewVolume(200, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") mvconfig := wtype.NewLHChannelParameter("P200Config", &minvol, &maxvol, &minspd, &maxspd, 1, false, wtype.LHVChannel, 0) mvadaptor := wtype.NewLHAdaptor("P200", "Gilson", mvconfig) minvol = wunit.NewVolume(2, "ul") maxvol = wunit.NewVolume(20, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") lmvconfig := wtype.NewLHChannelParameter("P20Config", &minvol, &maxvol, &minspd, &maxspd, 1, false, wtype.LHVChannel, 0) lmvadaptor := wtype.NewLHAdaptor("P20", "Gilson", lmvconfig) minvol = wunit.NewVolume(1, "ul") maxvol = wunit.NewVolume(10, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") lvconfig := wtype.NewLHChannelParameter("P10Config", &minvol, &maxvol, &minspd, &maxspd, 1, false, wtype.LHVChannel, 0) lvadaptor := wtype.NewLHAdaptor("P10", "Gilson", lvconfig) minvol = wunit.NewVolume(0.2, "ul") maxvol = wunit.NewVolume(2, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") vlvconfig := wtype.NewLHChannelParameter("P2Config", &minvol, &maxvol, &minspd, &maxspd, 1, false, wtype.LHVChannel, 0) vlvadaptor := wtype.NewLHAdaptor("P2", "Gilson", vlvconfig) minvol = wunit.NewVolume(0.2, "ul") maxvol = wunit.NewVolume(5000, "ul") headparams := wtype.NewLHChannelParameter("LabHand", &minvol, &maxvol, &minspd, &maxspd, 8, false, wtype.LHVChannel, 0) head := wtype.NewLHHead("LabHand", "MotherNature", headparams) head.Adaptor = hvadaptor lhp.Adaptors = append(lhp.Adaptors, hvadaptor) lhp.Adaptors = append(lhp.Adaptors, mvadaptor) lhp.Adaptors = append(lhp.Adaptors, lmvadaptor) lhp.Adaptors = append(lhp.Adaptors, lvadaptor) lhp.Adaptors = append(lhp.Adaptors, vlvadaptor) lhp.Heads = append(lhp.Heads, head) lhp.HeadsLoaded = append(lhp.HeadsLoaded, head) return lhp }
func makeGilson() *liquidhandling.LHProperties { // gilson pipetmax tips := GetTipList() layout := make(map[string]wtype.Coordinates) i := 0 x0 := 3.886 y0 := 3.513 z0 := -82.035 xi := 149.86 yi := 95.25 xp := x0 yp := y0 zp := z0 for y := 0; y < 3; y++ { for x := 0; x < 3; x++ { posname := fmt.Sprintf("position_%d", i+1) crds := wtype.Coordinates{xp, yp, zp} layout[posname] = crds i += 1 xp += xi } yp += yi } lhp := liquidhandling.NewLHProperties(9, "Pipetmax", "Gilson", "discrete", "disposable", layout) for _, tt := range tips { tb := GetTipByType(tt) if tb.Mnfr == lhp.Mnfr { lhp.Tips = append(lhp.Tips, tb.Tips[0][0]) } } lhp.Tip_preferences = []int{2, 3, 4} lhp.Input_preferences = []int{4, 5, 6} lhp.Output_preferences = []int{7, 8, 9} minvol := wunit.NewVolume(10, "ul") maxvol := wunit.NewVolume(250, "ul") minspd := wunit.NewFlowRate(0.5, "ml/min") maxspd := wunit.NewFlowRate(2, "ml/min") hvconfig := wtype.NewLHChannelParameter("HVconfig", &minvol, &maxvol, &minspd, &maxspd, 8, false, wtype.LHVChannel, 0) hvadaptor := wtype.NewLHAdaptor("DummyAdaptor", "Gilson", hvconfig) hvhead := wtype.NewLHHead("HVHead", "Gilson", hvconfig) hvhead.Adaptor = hvadaptor minvol = wunit.NewVolume(0.5, "ul") maxvol = wunit.NewVolume(50, "ul") minspd = wunit.NewFlowRate(0.1, "ml/min") maxspd = wunit.NewFlowRate(0.5, "ml/min") lvconfig := wtype.NewLHChannelParameter("LVconfig", &minvol, &maxvol, &minspd, &maxspd, 8, false, wtype.LHVChannel, 1) lvadaptor := wtype.NewLHAdaptor("DummyAdaptor", "Gilson", lvconfig) lvhead := wtype.NewLHHead("LVHead", "Gilson", lvconfig) lvhead.Adaptor = lvadaptor lhp.Heads = append(lhp.Heads, hvhead) lhp.Heads = append(lhp.Heads, lvhead) lhp.HeadsLoaded = append(lhp.HeadsLoaded, hvhead) lhp.HeadsLoaded = append(lhp.HeadsLoaded, lvhead) return lhp }
func choose_plate_assignments(component_volumes map[string]wunit.Volume, plate_types []*wtype.LHPlate, weight_constraint map[string]float64) map[string]map[*wtype.LHPlate]int { // // optimization is set up as follows: // // let: // Xk = Number of wells of type Y containing component Z (k = 1...YZ) // Vy = Working volume of well Y // RVy = Residual volume of well Y // TVz = Total volume of component Z required // WRy = Rate of wells of type y in their plate // PMax = Maximum number of plates // WMax = Maximum number of wells // // Minimise: // sum of Xk Wrv RVy // // Subject to: // sum of Xk Vy >= TVz for each component Z // sum of WRy Xk <= PMax // sum of Xk <= WMax // // setup lp := glpk.New() defer lp.Delete() lp.SetProbName("Assignments") lp.SetObjName("Z") // CHECK THIS lp.SetObjDir(glpk.MAX) // constraints: // total component volume // number of plates // number of wells n_rows := len(component_volumes) + 2 lp.AddRows(n_rows) cur := 1 component_order := make([]string, len(component_volumes)) // volume constraints for cmp, vol := range component_volumes { component_order[cur-1] = cmp lp.SetRowBnds(cur, glpk.LO, vol.ConvertTo(wunit.ParsePrefixedUnit("ul")), 99999.0) cur += 1 } // from now on we always have to use component_order // plate number constraints max_n_plates := weight_constraint["MAX_N_PLATES"] - 1.0 lp.SetRowBnds(cur, glpk.UP, -99999.0, max_n_plates) cur += 1 // well number constraints max_n_wells := weight_constraint["MAX_N_WELLS"] lp.SetRowBnds(cur, glpk.UP, -99999.0, max_n_wells) cur += 1 // set up the matrix columns num_cols := len(component_order) * len(plate_types) lp.AddCols(num_cols) cur = 1 for _, component := range component_order { for _, plate := range plate_types { // set up objective coefficient, column name and lower bound rv := plate.Welltype.ResidualVolume() coef := rv.ConvertTo(wunit.ParsePrefixedUnit("ul")) * weight_constraint["RESIDUAL_VOLUME_WEIGHT"] lp.SetObjCoef(cur, coef) lp.SetColName(cur, component+"_"+plate.PlateName) lp.SetColBnds(cur, glpk.LO, 0.0, 0.0) lp.SetColKind(cur, glpk.IV) cur += 1 } } // now set up the constraint coefficients cur = 1 ind := wutil.Series(0, num_cols) for c, _ := range component_order { row := make([]float64, num_cols+1) col := 0 for i := 0; i < len(component_order); i++ { for j := 0; j < len(plate_types); j++ { vc := 0.0 // pick out a set of columns according to which row we're on // volume constraints are the working volumes of the wells if c == i { vol := wunit.NewVolume(plate_types[j].Welltype.Vol, plate_types[j].Welltype.Vunit) rvol := wunit.NewVolume(plate_types[j].Welltype.Rvol, plate_types[j].Welltype.Vunit) vol.Subtract(&rvol) vc = vol.ConvertTo(wunit.ParsePrefixedUnit("ul")) } row[col+1] = vc col += 1 } } lp.SetMatRow(cur, ind, row) cur += 1 } // now the plate constraint row := make([]float64, num_cols+1) col := 1 for i := 0; i < len(component_order); i++ { for j := 0; j < len(plate_types); j++ { // the coefficient here is 1/the number of this well type per plate r := 1.0 / float64(plate_types[j].Nwells) row[col] = r col += 1 } } lp.SetMatRow(cur, ind, row) cur += 1 // finally the well constraint row = make([]float64, num_cols+1) col = 1 for i := 0; i < len(component_order); i++ { for j := 0; j < len(plate_types); j++ { // the number of wells is constrained so we just count row[col] = 1.0 col += 1 } } lp.SetMatRow(cur, ind, row) iocp := glpk.NewIocp() iocp.SetPresolve(true) iocp.SetMsgLev(0) lp.Intopt(iocp) // check constraints /* for i := 1; i <= n_rows; i++ { fmt.Println("ROW : ", i, " VAL : ", lp.MipRowVal(i)) } */ // fill assignments - this is the number of wells in the plate of each type needed assignments := make(map[string]map[*wtype.LHPlate]int, len(component_volumes)) cur = 1 for i := 0; i < len(component_order); i++ { cmap := make(map[*wtype.LHPlate]int) for j := 0; j < len(plate_types); j++ { nwells := lp.MipColVal(cur) if nwells > 0 { //fmt.Println(component_order[i], " : ", plate_types[j].Type, " N WELLS: ", nwells) cmap[plate_types[j]] = int(nwells) } cur += 1 } assignments[component_order[i]] = cmap } return assignments }
func main() { eid, _ := uuid.NewV4() em := equipmentManager.NewAnthaEquipmentManager(eid.String()) defer em.Shutdown() eem := equipmentManager.EquipmentManager(em) equipmentManager.SetEquipmentManager(&eem) //manual driver equipment mid, _ := uuid.NewV4() var mde equipment.Equipment var amd manual.AnthaManual amd = *manual.NewAnthaManual(mid.String()) mde = amd em.RegisterEquipment(&mde) //cui logger middleware cmw := middleware.NewLogToCui(&amd.Cui) log_id, _ := uuid.NewV4() l := logger.NewAnthaFileLogger(log_id.String()) l.RegisterMiddleware(cmw) var params Parameters var inputs Inputs // give this thing an arbitrary ID for testing id := execute.ThreadID(fmt.Sprintf("EXPERIMENT_1_%s", string(eid.String()[1:5]))) fmt.Println(id) // set up parameters and inputs params.Reactionvolume = wunit.NewVolume(20, "ul") params.Partconc = wunit.NewConcentration(0.0001, "g/l") params.Vectorconc = wunit.NewConcentration(0.001, "g/l") params.Atpvol = wunit.NewVolume(1, "ul") params.Revol = wunit.NewVolume(1, "ul") params.Ligvol = wunit.NewVolume(1, "ul") params.Reactiontemp = wunit.NewTemperature(25, "C") params.Reactiontime = wunit.NewTime(1800, "s") params.Inactivationtemp = wunit.NewTemperature(40, "C") params.Inactivationtime = wunit.NewTime(60, "s") params.BlockID = id inputs.Parts = make([]*wtype.LHComponent, 4) for i := 0; i < 4; i++ { inputs.Parts[i] = factory.GetComponentByType("dna_part") inputs.Parts[i].CName = inputs.Parts[i].CName + "_" + strconv.Itoa(i+1) } inputs.Vector = factory.GetComponentByType("standard_cloning_vector_mark_1") inputs.RestrictionEnzyme = factory.GetComponentByType("SapI") inputs.Ligase = factory.GetComponentByType("T4Ligase") inputs.Buffer = factory.GetComponentByType("CutsmartBuffer") inputs.ATP = factory.GetComponentByType("ATP") inputs.Outplate = factory.GetPlateByType("pcrplate") inputs.TipType = factory.GetTipboxByType("Gilson50") ctx := execution.GetContext() conf := make(map[string]interface{}) conf["MAX_N_PLATES"] = 1.5 conf["MAX_N_WELLS"] = 12.0 conf["RESIDUAL_VOLUME_WEIGHT"] = 1.0 conf["SQLITE_FILE_IN"] = "/Users/msadowski/synthace/protocol_language/checkout/synthace-antha/anthalib/driver/liquidhandling/pm_driver/default.sqlite" conf["SQLITE_FILE_OUT"] = "/tmp/output_file.sqlite" ctx.ConfigService.SetConfig(id, conf) outputs := Steps(params, inputs) fmt.Println(outputs.Reaction) }
func AdvancedExecutionPlanner(request *LHRequest, parameters *liquidhandling.LHProperties) *LHRequest { // in the first instance we assume this is done component-wise // we also need to identify dependencies, i.e. if certain components // are only available after other actions // this will only work if the components to be added are to go in the same order // get the layout groups minorlayoutgroups := request.Output_minor_group_layouts ass := request.Output_assignments inass := request.Input_assignments output_solutions := request.Output_solutions input_plates := request.Input_plates output_plate_layout := request.Output_plate_layout plate_lookup := request.Plate_lookup // highly inelegant... we should swap Tip Box Setup // around to take place after, then this whole thing // is a non-issue tt := make([]*wtype.LHTip, 1) tt[0] = request.Tip_Type.Tiptype parameters.Tips = tt instructions := liquidhandling.NewRobotInstructionSet(nil) // need to deal with solutions order := request.Input_order for _, name := range order { //fmt.Println(name) // cmpa holds a list of inputs required per destination // i.e. if destination X requires 15 ul of h2o this will be listed separately // at this point all of the requests must be for volumes, // we need a mapping from these to where they belong // do we? /* cmpa:=value.([]map[string]interface{}) cmp_map:=make(map[string]interface{}, len(cmpa)) for _,cmp:=range cmpa{ srcid:=cmp.Srcid cmp_map[srcid]=cmp } */ for n, g := range minorlayoutgroups { grp := []string(g) // get the group assignment string assignment := ass[n] // the assignment has the format plateID:row:column:incrow:inccol // where inc defines how the next one is to be calculated // e.g. {GUID}:A:1:1:0 // {GUID}:A:1:0:1 asstx := strings.Split(assignment, ":") plate := asstx[0] toplatenum := wutil.ParseInt(plate) row := wutil.AlphaToNum(asstx[1]) col := wutil.ParseInt(asstx[2]) incrow := wutil.ParseInt(asstx[3]) inccol := wutil.ParseInt(asstx[4]) whats := make([]string, len(grp)) pltfrom := make([]string, len(grp)) pltto := make([]string, len(grp)) plttypefrom := make([]string, len(grp)) plttypeto := make([]string, len(grp)) wellfrom := make([]string, len(grp)) wellto := make([]string, len(grp)) vols := make([]*wunit.Volume, len(grp)) fvols := make([]*wunit.Volume, len(grp)) tvols := make([]*wunit.Volume, len(grp)) for i, solID := range grp { sol := output_solutions[solID] // we need to get the relevant component out smpl := get_aggregate_component(sol, name) // we need to know where this component was assigned to inassignmentar := []string(inass[name]) inassignment, ok := get_assignment(inassignmentar, &input_plates, smpl.Vol) if !ok { wutil.Error(errors.New(fmt.Sprintf("No input assignment for %s with vol %-4.1f", name, smpl.Vol))) } inasstx := strings.Split(inassignment, ":") inplt := inasstx[0] inrow := string(inasstx[1]) incol := wutil.ParseInt(inasstx[2]) // we can fill the structure now whats[i] = name pltfrom[i] = plate_lookup[string(inplt)] pltto[i] = plate_lookup[output_plate_layout[toplatenum]] wellfrom[i] = inrow + strconv.Itoa(incol) wellto[i] = wutil.NumToAlpha(row) + strconv.Itoa(col) v := wunit.NewVolume(smpl.Vol, smpl.Vunit) v2 := wunit.NewVolume(0.0, "ul") vols[i] = &v // TODO Get the proper volumes here fvols[i] = &v2 tvols[i] = &v2 row += incrow col += inccol } ins := liquidhandling.NewTransferInstruction(whats, pltfrom, pltto, wellfrom, wellto, plttypefrom, plttypeto, vols, fvols, tvols /*, parameters.Cnfvol*/) instructions.Add(ins) } } inx := instructions.Generate(request.Policies, parameters) instrx := make([]liquidhandling.TerminalRobotInstruction, len(inx)) for i := 0; i < len(inx); i++ { instrx[i] = inx[i].(liquidhandling.TerminalRobotInstruction) } request.Instructions = instrx return request }
func NewGenericPhysical(mattertype string) GenericPhysical { gp := GenericPhysical{MatterByName(mattertype), mattertype, wunit.NewMass(0.0, "g"), wunit.NewVolume(0.0, "L"), wunit.NewTemperature(0.0, "˚C")} return gp }
func (w *LHWell) WorkingVolume() *wunit.Volume { v := wunit.NewVolume(w.Vol, w.Vunit) v2 := wunit.NewVolume(w.Rvol, w.Vunit) v.Subtract(&v2) return &v }
func (lhc *LHComponent) Volume() wunit.Volume { return wunit.NewVolume(lhc.Vol, lhc.Vunit) }
func solution_setup(request *LHRequest, prms *liquidhandling.LHProperties) (map[string]*wtype.LHSolution, map[string]float64) { solutions := request.Output_solutions // index of components used to make up to a total volume, along with the required total mtvols := make(map[string][]float64, 10) // index of components with concentration targets, along with the target concentrations mconcs := make(map[string][]float64, 10) // keep a list of components which have fixed stock concentrations fixconcs := make([]*wtype.LHComponent, 0) // maximum solubilities of each component Smax := make(map[string]float64, 10) // maximum total volume of any solution containing each component hshTVol := make(map[string]float64) // find the minimum and maximum required concentrations // across all the solutions for _, solution := range solutions { components := solution.Components // we need to identify the concentration components // and the total volume components, if we have // concentrations but no tvols we have to return // an error arrCncs := make([]*wtype.LHComponent, 0, len(components)) arrTvol := make([]*wtype.LHComponent, 0, len(components)) cmpvol := 0.0 totalvol := 0.0 for _, component := range components { // what sort of component is it? conc := component.Conc tvol := component.Tvol if conc != 0.0 { arrCncs = append(arrCncs, component) } else if tvol != 0.0 { tv := component.Tvol if totalvol == 0.0 || totalvol == tv { totalvol = tv } else { // error wutil.Error(errors.New(fmt.Sprintf("Inconsistent total volumes %-6.4f and %-6.4f at component %s", totalvol, tv, component.Name))) } } else { cmpvol += component.Vol } } // add everything to the maps for _, cmp := range arrCncs { nm := cmp.CName cnc := cmp.Conc _, ok := Smax[nm] if !ok { Smax[nm] = cmp.Smax } if cmp.StockConcentration != 0.0 { fixconcs = append(fixconcs, cmp) continue } var cncslc []float64 cncslc, ok = mconcs[nm] if !ok { cncslc = make([]float64, 0, 10) } cncslc = append(cncslc, cnc) mconcs[nm] = cncslc _, ok = hshTVol[nm] if !ok || hshTVol[nm] > totalvol { hshTVol[nm] = totalvol } } // now the total volumes for _, cmp := range arrTvol { nm := cmp.CName tvol := cmp.Tvol var tvslc []float64 tvslc, ok := mtvols[nm] if !ok { tvslc = make([]float64, 0, 10) } tvslc = append(tvslc, tvol) mtvols[nm] = tvslc } } // end solutions // so now we should be able to make stock concentrations // first we need the min and max for each minrequired := make(map[string]float64, len(mconcs)) maxrequired := make(map[string]float64, len(mconcs)) //TODO this needs to be migrated elsewhere var vmin wunit.Volume = wunit.NewVolume(1.0, "ul") if prms.CurrConf != nil { vmin = *(prms.CurrConf.Minvol) } for cmp, arr := range mconcs { min := wutil.FMin(arr) max := wutil.FMax(arr) minrequired[cmp] = min maxrequired[cmp] = max // if smax undefined we need to deal - we assume infinite solubility!! _, ok := Smax[cmp] if !ok { Smax[cmp] = 9999999 wutil.Warn(fmt.Sprintf("Max solubility undefined for component %s -- assuming infinite solubility!", cmp)) } } stockconcs := choose_stock_concentrations(minrequired, maxrequired, Smax, vmin.RawValue(), hshTVol) // handle any errors here // add the fixed concentrations into stockconcs for _, cmp := range fixconcs { stockconcs[cmp.CName] = cmp.StockConcentration } // nearly there now! Need to turn all the components into volumes, then we're done // make an array for the new solutions newSolutions := make(map[string]*wtype.LHSolution, len(solutions)) for _, solution := range solutions { components := solution.Components arrCncs := make([]*wtype.LHComponent, 0, len(components)) arrTvol := make([]*wtype.LHComponent, 0, len(components)) arrSvol := make([]*wtype.LHComponent, 0, len(components)) cmpvol := 0.0 totalvol := 0.0 totalvolunit := "" for _, component := range components { // what sort of component is it? // what is the total volume ? if component.Conc != 0.0 { arrCncs = append(arrCncs, component) } else if component.Tvol != 0.0 { arrTvol = append(arrTvol, component) tv := component.Tvol totalvolunit = component.Vunit if totalvol == 0.0 || totalvol == tv { totalvol = tv } else { // error wutil.Error(errors.New(fmt.Sprintf("Inconsistent total volumes %-6.4f and %-6.4f at component %s", totalvol, tv, component.Name))) } } else { // need to add in the volume taken up by any volume components cmpvol += component.Vol arrSvol = append(arrSvol, component) } } // first we add the volumes to the concentration components arrFinalComponents := make([]*wtype.LHComponent, 0, len(components)) for _, component := range arrCncs { name := component.CName cnc := component.Conc vol := totalvol * cnc / stockconcs[name] cmpvol += vol component.Vol = vol component.Vunit = totalvolunit component.StockConcentration = stockconcs[name] arrFinalComponents = append(arrFinalComponents, component) } // next we get the final volume for total volume components for _, component := range arrTvol { vol := totalvol - cmpvol component.Vol = vol arrFinalComponents = append(arrFinalComponents, component) } // then we add the rest arrFinalComponents = append(arrFinalComponents, arrSvol...) // finally we replace the components in this solution solution.Components = arrFinalComponents // and put the new solution in the array newSolutions[solution.ID] = solution } return newSolutions, stockconcs }