finesse.py 98.9 KB
Newer Older
1001
1002
        
        if name not in self.__blocks:
Daniel Brown's avatar
updates    
Daniel Brown committed
1003
1004
1005
1006
1007
            if failOnBlockNotFound:
                pkex.PrintError("Error removing block:", pkex.BasePyKatException('Block "{0}" was not found'.format(name)))
                sys.exit(1)
            else:
                return
1008
1009
        
        for o in self.__blocks[name].contents.copy():
1010
1011
1012
            self.remove(o)
        
        del self.__blocks[name]
1013
1014
1015
1016
    
    def __str__(self):
         return "".join(self.generateKatScript())
         
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
    def getVariable(self, name):
        if name not in self.__variables:
            raise pkex.BasePyKatException("Finesse variable `$%s` does not exist." % name)
            
        return self.__variables[name]
    
    def registerVariable(self, name, putter):
        if '$' in name:
            raise pkex.BasePyKatException("Finesse variable name `%s` should not include the `$` symbol as it is added internally." % name)
            
        assert(putter is not None)
        assert(name == putter.name)
        
        if name in self.__variables:
            raise pkex.BasePyKatException("Finesse variable name `%s` already exists." % name)
            
        self.__variables[name] = putter
        
    def unregisterVariable(self, name):
        del self.__variables[name]
    
    def printVariables(self):
        for key in self.__variables:
            print("$" + key, "::::", "owner =", self.__variables[key].owner.name, ", use count =", self.__variables[key].putCount)
    
1042
    def parseCommands(self, commands, blocks=None, addToBlock=None):
1043
        try:
1044
1045
1046
1047
1048
1049
1050
1051
            if addToBlock is not None and blocks is not None:
                raise pkex.BasePyKatException("When parsing commands you cannot set both blocks and addToBlock arguments")
            
            # Create a new block if one asked for isn't present
            if addToBlock is not None:
                if addToBlock not in self.__blocks:
                    self.__blocks[addToBlock] = Block(addToBlock)
                
1052
            blockComment = False
Daniel Brown's avatar
Daniel Brown committed
1053
        
1054
            commands=self.remove_comments(commands)
1055
        
1056
            commands=self.processConstants(commands)
Daniel Brown's avatar
Daniel Brown committed
1057
        
1058
1059
            after_process = [] # list of commands that should be processed after 
                               # objects have been set and created
Daniel Brown's avatar
Daniel Brown committed
1060
        
1061
            for line in commands:
1062
1063
1064
1065
1066
                if len(line.strip()) >= 2:
                    line = line.strip()

                    # Looking for block start or end
                    values = line.split()
1067
                    
1068
1069
1070
1071
1072
1073
1074
                    if addToBlock is None:
                        if values[0] == "%%%":
                            if values[1] == "FTblock":
                                newTag = values[2]
                    
                                if self.__currentTag != None and self.__currentTag != NO_BLOCK: 
                                    warnings.warn("found block {0} before block {1} ended".format(newTag, self.__currentTag))    
1075
                        
1076
1077
                                if newTag in self.__blocks:
                                    raise pkex.BasePyKatException("Block `{0}` has already been read".format(newTag))
1078
                        
1079
1080
                                self.__blocks[newTag] = Block(newTag) # create new list to store all references to components in block
                                self.__currentTag = newTag
1081
                    
1082
1083
                            if values[1] == "FTend":
                                self.__currentTag = NO_BLOCK
1084
                    
1085
1086
1087
                            continue
                    else:
                        self.__currentTag = addToBlock
Daniel Brown's avatar
Daniel Brown committed
1088

1089
1090
1091
                    # only include listed blocks, if we have specfied them
                    if blocks != None and self.__currentTag not in blocks:
                        continue
1092
                
1093
1094
1095
                    # don't read comment lines
                    if line[0] == "#" or line[0] == "%":
                        continue
1096
            
1097
1098
1099
1100
1101
1102
1103
                    # check if block comment is being used
                    if not blockComment and line[0:2] == "/*":
                        blockComment = True
                        continue
                    elif blockComment and line[0:2] == "*/":
                        blockComment = False
                        continue
1104
            
1105
1106
                    first = line.split(" ",1)[0]
                    obj = None
1107

1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
                    if(first == "m" or first == "m1" or first == "m2"):
                        obj = pykat.components.mirror.parseFinesseText(line)
                    elif(first == "s"):
                        obj = pykat.components.space.parseFinesseText(line)
                    elif(first == "l"):
                        obj = pykat.components.laser.parseFinesseText(line)
                    elif(first == "sq"):
                        obj = pykat.components.squeezer.parseFinesseText(line)
                    elif(first[0:2] == "bs"):
                        obj = pykat.components.beamSplitter.parseFinesseText(line)
                    elif(first[0:2] == "gr"):
                        obj = pykat.components.grating.parseFinesseText(line)
                    elif(first[0:4] == "isol"):
                        obj = pykat.components.isolator.parseFinesseText(line)
                    elif(first[0:4] == "lens"):
                        obj = pykat.components.lens.parseFinesseText(line)
                    elif(first[0:3] == "mod"):
                        obj = pykat.components.modulator.parseFinesseText(line)
                    elif(first[0:2] == "ad"):
                        obj = pykat.detectors.ad.parseFinesseText(line)
Daniel Brown's avatar
Daniel Brown committed
1128
1129
1130
1131
                    elif(first[0:2] == "xd"):
                        obj = pykat.detectors.xd.parseFinesseText(line)
                    elif(first[0:2] == "tf"):
                        obj = pykat.commands.tf.parseFinesseText(line)
1132
1133
                    elif(first[0:2] == "cp"):
                        obj = pykat.detectors.cp.parseFinesseText(line)
1134
1135
1136
1137
                    elif(first[0:2] == "bp"):
                        obj = pykat.detectors.bp.parseFinesseText(line)
                    elif(first[0:4] == "gouy"):
                        obj = pykat.detectors.gouy.parseFinesseText(line)
Daniel Brown's avatar
Daniel Brown committed
1138
1139
                    elif(first[0:4] == "beam"):
                        obj = pykat.detectors.beam.parseFinesseText(line)
1140
1141
1142
1143
1144
1145
1146
                    elif(first[0:2] == "pd" and first != "pdtype"):
                        obj = pykat.detectors.pd.parseFinesseText(line)
                    elif(first == "qshot" or first == "qshotS" or first == "qshotN"):
                        obj = pykat.detectors.qshot.parseFinesseText(line)
                    elif(first == "qnoised" or first == "qnoisedS" or first == "qnoisedN"):
                        obj = pykat.detectors.qnoised.parseFinesseText(line)
                    elif(first == "xaxis" or first == "xaxis*"):
1147
                        self.noxaxis = False
1148
                        obj = pykat.commands.xaxis.parseFinesseText(line)
1149
1150
                    elif(first[0:2] == "hd"):
                        obj = pykat.detectors.hd.parseFinesseText(line)
1151
1152
                    elif(first.startswith("qhd")):
                        obj = pykat.detectors.qhd.parseFinesseText(line)
1153
1154
1155
                    elif(first == "x2axis" or first == "x2axis*"):
                        obj = pykat.commands.x2axis.parseFinesseText(line)
                    elif(first == "gauss" or first == "gauss*" or first == "gauss**"):
1156
                        after_process.append((line, self.__currentTag))
1157
                    elif(first == "scale"):
1158
                        after_process.append((line, self.__currentTag))
1159
                    elif(first == "pdtype"):
1160
                        after_process.append((line, self.__currentTag))
1161
                    elif(first == "cav"):
1162
1163
1164
1165
1166
1167
1168
                        after_process.append((line, self.__currentTag))
                    elif(first == "func"):
                        after_process.append((line, self.__currentTag))
                    elif(first == "variable"):
                        after_process.append((line, self.__currentTag))
                    elif(first == "lock"):
                        after_process.append((line, self.__currentTag))
1169
                    elif(first == "attr"):
1170
                        after_process.append((line, self.__currentTag))
1171
1172
1173
1174
1175
1176
                    elif(first == "noxaxis"):
                        self.noxaxis = True
                    elif(first == "lambda"):
                        v = line.split()
                        self.lambda0 = SIfloat(v[-1])
                    elif(first == "yaxis"):
Daniel Brown's avatar
Daniel Brown committed
1177
                        v = line.split(" ", 1)
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
                        self.yaxis = v[-1]
                    elif(first == "phase"):
                        v = line.split()
                        if len(v) != 2:
                            raise pkex.BasePyKatException("phase command `{0}` is incorrect.".format(line))
                        else:
                            self.phase = int(v[1])
                    elif(first == "maxtem"):
                        v = line.split()
                        if len(v) != 2:
                            raise pkex.BasePyKatException("maxtem command `{0}` is incorrect.".format(line))
                        else:
                            if v[1] == "off":
1191
                                self.maxtem = -1
1192
                            else:
1193
                                self.maxtem = int(v[1])
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
                    elif(first == "trace"):
                        v = line.split()
                        if len(v) > 2:
                            raise pkex.BasePyKatException("Trace command `{0}` is incorrect.".format(line))
                        elif len(v) == 2:
                            self.trace = v[1]
                    elif(first == "retrace"):
                        v = line.split()
                        if len(v) > 2:
                            raise pkex.BasePyKatException("Retrace command `{0}` is incorrect.".format(line))
                        elif len(v) == 2:
                            self.retrace = v[1]                        
                    elif(first == "deriv_h"):
                        v = line.split()
                        if len(v) != 2:
                            raise pkex.BasePyKatException("deriv_h command `{0}` is incorrect.".format(line))
1210
                        else:
1211
1212
1213
                            self.deriv_h = float(v[1])
                    elif(first == "gnuterm" or first == "pyterm"):
                        if self.verbose:
1214
                            print ("Ignoring Gnuplot/Python terminal command '{0}'".format(line))
Daniel Brown's avatar
Daniel Brown committed
1215
                    elif(first == "fsig"):
1216
                        after_process.append((line, self.__currentTag))
1217
                    elif(first == "noplot"):
1218
                        after_process.append((line, self.__currentTag))
1219
1220
                    elif(first == "put" or first == "put*"):
                        after_process.append((line, self.__currentTag))
1221
                    else:
1222
                        if self.verbose:
1223
                            print ("Parsing `{0}` into pykat object not implemented yet, added as extra line.".format(line))
1224
                    
1225
1226
1227
                        obj = line
                        # manually add the line to the block contents
                        self.__blocks[self.__currentTag].contents.append(line) 
1228
            
1229
                    if obj != None and not isinstance(obj, six.string_types):
1230
1231
                        if self.hasNamedObject(obj.name):
                            getattr(self, obj.name).remove()
1232
                            print ("Removed existing object '{0}' of type {1} to add line '{2}'".format(obj.name, obj.__class__, line))
Daniel Brown's avatar
Daniel Brown committed
1233

1234
                        self.add(obj, block=self.__currentTag)
1235
                
1236
1237
1238
1239
1240
1241
1242
            
            # Before processing the rest, all "noplot" commands are moved to the
            # end of the list to make sure they are after all "func" commands.
            for k in range(len(after_process)-1,-1,-1):
                if after_process[k][0].split(" ", 1)[0] == "noplot":
                    after_process.append(after_process.pop(k))

1243
1244
            # now process all the varous gauss/attr etc. commands which require
            # components to exist first before they can be processed
1245
1246
            for item in after_process:
                line = item[0]
1247
                first, rest = line.split(" ",1)
1248
1249
                block = item[1]
                
1250
1251
                if first == "gauss" or first == "gauss*" or first == "gauss**":
                    pykat.commands.gauss.parseFinesseText(line, self)
1252
1253
                    
                elif (first == "cav"):
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
                    self.add(pykat.commands.cavity.parseFinesseText(line, self), block=block)
                    
                elif (first == "lock"):
                    self.add(pykat.commands.lock.parseFinesseText(line, self), block=block)
                    
                elif (first == "func"):
                    self.add(pykat.commands.func.parseFinesseText(line, self), block=block)
                    
                elif (first == "variable"):
                    self.add(pykat.commands.variable.parseFinesseText(line, self), block=block)
1264
                    
1265
1266
1267
1268
1269
                elif (first == "noplot"):
                    if not hasattr(self, rest):
                        raise pkex.BasePyKatException("noplot command `{0}` refers to non-existing detector".format(line))
                        
                    getattr(self, rest).noplot = True
1270
1271
1272
                
                elif (first == "put" or first =="put*"):
                    alt = first == "put*"
1273
                    
1274
1275
1276
1277
1278
                    values = line.split()
                    obj = values[1]
                    target = values[2]
                    variable = values[3]
                    
1279
1280
1281
                    try:
                        if not hasattr(self, obj):
                            raise pkex.BasePyKatException("put command `{0}` refers to non-existing component".format(line))
1282
                    
1283
                        obj = getattr(self, obj)
1284
                    
1285
1286
                        if not hasattr(obj, target):
                            raise pkex.BasePyKatException("put command component `{0}` does not have a parameter `{1}`".format(line, target))
1287
                        
1288
                        target = getattr(obj, target)
1289
                    
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
                        if not target.isPutable:
                            raise pkex.BasePyKatException("put command `{0}` parameter `{1}` cannot be put to".format(line, target))
                        
                        target.put(self.getVariable(variable.replace('$', '')), alt)
                        
                    except pkex.BasePyKatException as ex:
                        if self.verbose:
                            print("Warning: ", ex.msg)
                            print ("Parsing `{0}` into pykat object not implemented yet, added as extra line.".format(line))
                    
                        obj = line
                        # manually add the line to the block contents
                        self.__blocks[block].contents.append(line)
1303
1304
                        
                 
1305
1306
                elif (first == "scale"):
                    v = line.split()
1307
                    accepted = ["psd","psd_hf","asd","asd_hf","meter", "ampere", "deg", "rad", "1/deg", "1/rad",]
Daniel Brown's avatar
Daniel Brown committed
1308
                
1309
1310
                    if len(v) == 3:
                        component_name = v[2]
Daniel Brown's avatar
Daniel Brown committed
1311
                    
1312
1313
1314
1315
1316
1317
1318
                        if v[1].lower() in accepted:
                            val = v[1]
                        else:
                            try:
                                val = SIfloat(v[1])
                            except ValueError as ex:
                                raise pkex.BasePyKatException("Line `{0}`:\nAccepted scale values are decimal numbers or %s." % (line,str(accepted)))
Daniel Brown's avatar
Daniel Brown committed
1319
                            
1320
1321
1322
1323
1324
1325
1326
1327
1328
                        if component_name in self.__detectors :
                            self.__detectors[component_name].scale.append(val)
                        else:
                            raise pkex.BasePyKatException("scale command `{0}` refers to non-existing output".format(component_name))
                    elif len(v) == 2:
                        if v[1] == "meter" or v[1] == "ampere" or v[1] == "deg":
                            self.scale = v[1]
                        else:
                            self.scale = SIfloat(v[1])
1329
                    else:
1330
1331
1332
1333
1334
1335
1336
1337
1338
                        raise pkex.BasePyKatException("scale command `{0}` is incorrect.".format(line))
                elif (first == "pdtype"):
                    v = line.split()
                    if len(v) == 3:
                        component_name = v[1]
                        if component_name in self.__detectors :
                            self.__detectors[component_name].pdtype = v[2]
                        else:
                            raise pkex.BasePyKatException("pdtype command `{0}` refers to non-existing detector".format(component_name))
1339
                    else:
1340
1341
1342
                        raise pkex.BasePyKatException("pdtype command `{0}` is incorrect.".format(line))
                elif(first == "attr"):
                    v = line.split()
1343

1344
1345
                    if len(v) < 4:
                        raise pkex.BasePyKatException("attr command `{0}` is incorrect.".format(line))
Daniel Brown's avatar
Daniel Brown committed
1346
                    else:
1347
1348
1349
1350
1351
1352
1353
                        # get the component/detector in question
                        if v[1] in self.__components:
                            comp = self.__components[v[1]]
                        elif v[1] in self.__detectors:
                            comp = self.__detectors[v[1]]
                        else:
                            raise pkex.BasePyKatException("Could not find the component '{0}' for attr command in line '{1}'".format(v[1], line))
Daniel Brown's avatar
Daniel Brown committed
1354
                
1355
1356
                        if len(v[2:]) % 2 == 1:
                            raise pkex.BasePyKatException("Attr command '{0}' must specify both parameter and value pairs".format(line))
Daniel Brown's avatar
Daniel Brown committed
1357
                                                
1358
1359
1360
                        # convert split list to key value pairs
                        #kv = dict(itertools.izip_longest(*[iter(v[2:])] * 2, fillvalue=None))
                        kv = dict(izip_longest(*[iter(v[2:])] * 2, fillvalue=None))
Daniel Brown's avatar
Daniel Brown committed
1361

1362
                        comp.parseAttributes(kv)
1363
                    
1364
                elif(first == "fsig"):
1365
                
1366
                    v = line.split()
1367
                
1368
                    name = str(v[1])
Daniel Brown's avatar
Daniel Brown committed
1369
                
1370
1371
1372
1373
1374
1375
                    if len(v) == 3:
                        self.signals._default_name = name
                        self.signals.f = SIfloat(v[2])
                    else:
                        if v[2] not in self.__components:
                            raise pkex.BasePyKatException("Could not find the component '{0}'. Line: '{1}'".format(v[2], line))
1376
                
1377
                        comp = self.__components[v[2]]
Daniel Brown's avatar
Daniel Brown committed
1378
                
1379
1380
                        if comp._default_fsig() is None:
                            raise pkex.BasePyKatException("Component '{0}' cannot be fsig'd. Line: '{1}'".format(comp.name, line))
Daniel Brown's avatar
Daniel Brown committed
1381
                    
1382
1383
                        param_name = None
                        amp = None
Daniel Brown's avatar
Daniel Brown committed
1384
                    
1385
1386
1387
1388
1389
                        if len(v) == 3:
                            self.signals._default_name = name
                            freq = SIfloat(v[3])
                        elif len(v) == 5:
                            #param is None
Daniel Brown's avatar
Daniel Brown committed
1390
1391
                            freq = SIfloat(v[3])
                            phase = SIfloat(v[4])
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
                        elif len(v) == 6:
                        
                            try:
                                SIfloat(v[3])
                                isFloat = True
                            except:
                                isFloat = False
                            
                            if isFloat:
                                freq = SIfloat(v[3])
                                phase = SIfloat(v[4])
                                amp = SIfloat(v[5])
                            else:
                                param_name = v[3]
                                freq = SIfloat(v[4])
                                phase = SIfloat(v[5])
                        
                        elif len(v) == 7:
1410
                            param_name = v[3]
Daniel Brown's avatar
Daniel Brown committed
1411
1412
                            freq = SIfloat(v[4])
                            phase = SIfloat(v[5])
1413
1414
1415
                            amp = SIfloat(v[6])
                        else:
                            raise pkex.BasePyKatException("'{0}' isnot a valid fsig command".format(line))
1416
                    
1417
                        self.signals.f = freq
1418
                    
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
                        param = None
                
                        if param_name is None:
                            param = comp._default_fsig()
                        else:
                            for p in comp._params:
                                if p.canFsig and p.fsigName == param_name:
                                    param = p
                                    break
                    
                            if param is None:
                                raise pkex.BasePyKatException("Line: '{0}': {1} is not a valid fsig target for {2}".format(line, param_name, comp.name))
1431
                        
1432
                        self.signals.apply(param, amp, phase, name)
1433
                
1434
1435
                else:
                    raise pkex.BasePyKatException("Haven't handled parsing of '{0}'".format(line))
1436
                    
1437
            self.__currentTag = NO_BLOCK 
1438
        
1439
1440

        except pkex.BasePyKatException as ex:
1441
            pkex.PrintError("Pykat error parsing line: '%s':"%  line, ex)
1442
1443
            sys.exit(1)
            
Daniel Brown's avatar
Daniel Brown committed
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
    def saveScript(self, filename=None):
        """
        Saves the current kat object to a Finesse input file
        """
        try:
            katScript = "".join(self.generateKatScript())       
            katfile = open(filename,'w')
            katfile.writelines(katScript)
            katfile.flush()
            katfile.close()

        except pkex.BasePyKatException as ex:
1456
            print (ex)
Daniel Brown's avatar
Daniel Brown committed
1457

1458
    def run(self, plot=None, save_output=False, save_kat=False, kat_name=None, cmd_args=None, getTraceData=False, rethrowExceptions=False):
Daniel Brown's avatar
Daniel Brown committed
1459
1460
1461
1462
        """ 
        Runs the current simulation setup that has been built thus far.
        It returns a katRun or katRun2D object which is populated with the various
        data from the simulation run.
1463
1464
1465
1466
1467
        plot (string) - Sets gnuterm for plotting
        save_output (bool) - if true does not delete out file
        save_kat (bool) - if true does not delete kat file
        kat_name (string) - name of kat file if needed, will be randomly generated otherwise
        cmd_args (list of strings) - command line flags to pass to FINESSE
1468
1469
1470
1471
1472
        getTraceData (bool) - If true a list of dictionaries is returned along with the
        output file. Each dictionary is the result of the beam tracing
        that Finesse performs, the keys are the node names and the values
        are the x and y beam parameters. If no tracing is done a None
        is returned.
1473
1474
        
        rethrowExceptions - if true exceptions will be thrown again rather than being excepted and calling sys.exit()
Daniel Brown's avatar
Daniel Brown committed
1475
        """
1476
        start = time.time()
Daniel Brown's avatar
Daniel Brown committed
1477
1478
1479
1480
1481
1482
1483
1484
1485
        
        try:        
            if not hasattr(self, "xaxis") and self.noxaxis != None and self.noxaxis == False:
                raise pkex.BasePyKatException("No xaxis was defined")
            
            if len(self.__katdir) == 0:
                # Get the environment variable for where Finesse is stored
                self.__finesse_dir = os.environ.get('FINESSE_DIR')
                
1486
                if self.__finesse_dir is None :
1487
                    raise pkex.MissingFinesseEnvVar()
Daniel Brown's avatar
Daniel Brown committed
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
            else:
                self.__finesse_dir = self.__katdir
                
            if len(self.__katname) == 0:
                katexe = "kat"
                
                if os.sys.platform == "win32":
                    katexe += ".exe"
            else:
                katexe = self.__katname
            
            kat_exec = os.path.join(self.__finesse_dir, katexe) 
            
            # check if kat file exists and it is executable by user        
            if not (os.path.isfile(kat_exec) and os.access(kat_exec, os.X_OK)):
1503
                raise pkex.MissingFinesse()
Daniel Brown's avatar
Daniel Brown committed
1504
                
1505
            if self.verbose: print ("--------------------------------------------------------------")
1506
            if self.verbose: print ("Running kat - Started at " + str(datetime.datetime.fromtimestamp(start)))
Daniel Brown's avatar
Daniel Brown committed
1507
            
1508
            if hasattr(self, "x2axis") and self.noxaxis == False:
Daniel Brown's avatar
Daniel Brown committed
1509
1510
1511
1512
                r = katRun2D()
            else:
                r = katRun()
                
1513
            r.yaxis = self.yaxis
1514
            
1515
            r.katScript = "".join(self.generateKatScript())
1516
1517
            r.katScript += "time\n"

1518
1519
1520
1521
1522
1523
1524
            if (plot==None):
                # ensure we don't do any plotting. That should be handled
                # by user themselves
                r.katScript+=("gnuterm no\n")
                r.katScript+=("pyterm no\n")
            else:
                r.katScript+=(plot+"\n")
Daniel Brown's avatar
Daniel Brown committed
1525
1526
            
            # create a kat file which we will write the script into
1527
            if self.__tempname is None:
Daniel Brown's avatar
Daniel Brown committed
1528
                katfile = tempfile.NamedTemporaryFile(mode ='w', suffix=".kat", dir=self.__tempdir, delete=False)
Daniel Brown's avatar
Daniel Brown committed
1529
1530
1531
1532
1533
            else:
                filepath =os.path.join(self.__tempdir, self.__tempname+".kat" )
                katfile = open( filepath, 'w' ) 
                
            katfile.writelines(r.katScript)
1534
            
Daniel Brown's avatar
Daniel Brown committed
1535
            katfile.flush()
1536

1537
1538
            pipe_name = katfile.name + str(uuid.uuid4())
            
Daniel Brown's avatar
Daniel Brown committed
1539
            cmd=[kat_exec, "--pykat=" + pipe_name]
Daniel Brown's avatar
Daniel Brown committed
1540
1541
1542
            
            if self.__time_code:
                cmd.append('--perf-timing')
1543
1544
1545
            
            if cmd_args != None:
                cmd.extend(cmd_args)
1546
1547
1548

            if getTraceData:
                cmd.append('--trace')
1549
                
Daniel Brown's avatar
Daniel Brown committed
1550
1551
1552
1553
1554
1555
1556
            cmd.append('--no-backspace')
            # set default format so that less repeated numbers are printed to the
            # output file, should speed up running and parsing of output files
            cmd.append('-format=%.15g')

            cmd.append(katfile.name)
            
1557
1558
1559
1560
1561
1562
1563
            if sys.platform == "win32" or sys.platform == "cygwin":
            	# Pipes in windows need to be prefixed with a hidden location.
            	pipe_name = "\\\\.\\pipe\\" + pipe_name

            p = Popen(cmd, stderr=PIPE, stdout=PIPE)

            if self.verbose:
1564
1565
1566
1567
1568
1569
1570
1571
                if self.noxaxis:
                    maxval = 1
                else:
                    maxval = 100
                    
                widgets = [progressbar.Percentage(), ' | ', progressbar.ETA(), ' | ', 'Status']
                
                pb = progressbar.ProgressBar(widgets=widgets, maxval = maxval)
1572
1573
1574

            fifo = None

1575
1576
            _start_kat = time.time()
            
Daniel Brown's avatar
Daniel Brown committed
1577
            duration = 2 # Duration for searching for open pipe
Daniel Brown's avatar
Daniel Brown committed
1578
            
1579
1580
1581
            try:
                while fifo is None:
                    try:
1582
                    	if time.time() < _start_kat + duration:
1583
                    		time.sleep(0.1)
Daniel Brown's avatar
Daniel Brown committed
1584
                    		fifo = codecs.open(pipe_name, "r", "utf-8")
Daniel Brown's avatar
Daniel Brown committed
1585
                    		self.__looking = False
1586
                    	else:
Daniel Brown's avatar
Daniel Brown committed
1587
                    		raise pkex.BasePyKatException("Could not connect to pykat pipe in {0} seconds. Ensure you are using Finesse >= v2.1 and Pykat >= v1.0.0.".format(duration))
1588
1589
                    except FileNotFoundError as ex:
                    	if self.verbose:
Daniel Brown's avatar
Daniel Brown committed
1590
1591
1592
                            if not self.__looking:
                                print("Looking for pykat pipe...")
                                self.__looking = True
1593
1594

                for line in fifo:
1595
                    
1596
1597
                    #if (sys.version_info < (3, 0)):
                    #    line = line.decode("utf8") # Make sure we're using unicode encoding
1598
1599
                    
                    v = line.split(u":", 1)
1600
                    
1601
1602
                    if len(v) != 2:
                        continue    
Daniel Brown's avatar
Daniel Brown committed
1603
                        
1604
1605
1606
1607
                    (tag, line) = v
                    
                    if tag == "version":
                        r.katVersion = line
1608
                    elif tag == "progress" and self.verbose:
1609
                        var = line.split("\t")
1610
1611
                        
                        if len(var) == 3:
1612
                        	pb.currval = int(var[1])
1613
                        	pb.widgets[-1] = var[0] + " " + var[2][:-1]
1614
1615
1616
1617
1618
1619
1620
                        	pb.update()
            finally:
            	if fifo is not None:
            		fifo.close()
			
            (stdout, stderr) = p.communicate()

1621
1622
            r.stdout = stdout.decode('utf-8')
            r.stderr = stderr.decode('utf-8')
Daniel Brown's avatar
Daniel Brown committed
1623
            
1624
            k = r.stdout.rfind('computation time:')
Daniel Brown's avatar
Daniel Brown committed
1625
            
1626
1627
1628
1629
1630
1631
1632
            if k > 0:
                try:
                    line = r.stdout[k:]
                    r.runtime = float(line.split(":")[1].replace("s",""))
                except:
                    r.runtime = 0.0
    
1633
1634
            r.runDateTime = datetime.datetime.now()

1635
            # If Finesse returned an error, just print that and exit!
Daniel Brown's avatar
Daniel Brown committed
1636
            if p.returncode != 0:
Daniel Brown's avatar
Daniel Brown committed
1637
                raise pkex.FinesseRunError(r.stderr, katfile.name)
1638
            
1639
1640
            self.__prevrunfilename = katfile.name
            
Daniel Brown's avatar
Daniel Brown committed
1641
            root = os.path.splitext(katfile.name)
1642
1643
            base = os.path.basename(root[0])
            path = os.path.split(katfile.name)[0]            
Daniel Brown's avatar
Daniel Brown committed
1644
            outfile = root[0] + ".out"
1645
1646
1647
1648
1649
1650
1651
1652

            traceData = None
            
            if getTraceData:
                # First see if we have any trace files
                
                traceFiles = [file for file in os.listdir(path) if file.endswith(".trace") and file.startswith(base)]
                
Daniel Brown's avatar
Daniel Brown committed
1653
1654
1655
                #print("Found %i trace files" % len(traceFiles))
                #print(path)
                #print(traceFiles)
1656
1657
1658
1659
1660
1661
1662
                
                if len(traceFiles) > 0:
                    import fileinput
                    traceData = []
                    
                    for file in traceFiles:
                        traceData.append({})
1663
1664
                        try:
                            ifile = fileinput.input(os.path.join(path, file))
1665
                    
1666
1667
                            for line in ifile:
                                line = line.strip()
1668
                        
1669
1670
                                if len(line) > 0:
                                    a = line.split(':', 1)
1671
                        
1672
1673
                                    if a[0].isdigit():
                                        #print("Found %s" % a[0])
1674
                                
1675
                                        values = a[1].split()
1676
                                
1677
                                        node_name = values[1].split("(")[0]
1678
                                
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
                                        line1x = ifile.readline().replace('(','').replace(')','')
                                        line2x = ifile.readline().replace('(','').replace(')','')
                                        line1y = ifile.readline().replace('(','').replace(')','')
                                        line2y = ifile.readline().replace('(','').replace(')','')
                                        
                                        spqx = line2x.strip().split("gamma")
                                        spqy = line2y.strip().split("gamma")
                                        
                                        qx = spqx[0].split("=")[1].replace('i','j').replace(' ','') 
                                        qy = spqy[0].split("=")[1].replace('i','j').replace(' ','') 
                                        
                                        traceData[-1][node_name] = (pykat.beam_param(q=complex(qx)), pykat.beam_param(q=complex(qy)))
                            
                        finally:
                            ifile.close()
1694

Daniel Brown's avatar
Daniel Brown committed
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
            
            if save_output:        
                newoutfile = "{0}.out".format(base)
                
                cwd = os.path.os.getcwd()
                newoutfile = os.path.join(cwd,newoutfile)
                
                if os.path.isfile(newoutfile):
                    os.remove(newoutfile)
                    
                os.rename(outfile, newoutfile)

1707
                if self.verbose: print ("\nOutput data saved to '{0}'".format(newoutfile))
1708

1709
1710
1711
1712
1713
            # can't see why this check is needed, causes problems when only detectors
            # not parsed as pykat objects are used
            #if len(self.detectors.keys()) > 0: 
            
            if hasattr(self, "x2axis") and self.noxaxis == False:
1714
                [r.x, r.y, r.z, hdr] = self.readOutFile(outfile)
Daniel Brown's avatar
Daniel Brown committed
1715
            
1716
1717
1718
1719
1720
                r.xlabel = hdr[0]
                r.ylabel = hdr[1]
                r.zlabels = [s.strip() for s in hdr[2:]]
                #r.zlabels = map(str.strip, hdr[2:])
            else:
1721
                [r.x, r.y, hdr] = self.readOutFile(outfile)
1722
1723
1724
1725
                
                r.xlabel = hdr[0]
                r.ylabels = [s.strip() for s in hdr[1:]]
                #r.ylabels = map(str.strip, hdr[1:]) // replaced 090415 adf 
1726
                    
Daniel Brown's avatar
Daniel Brown committed
1727
            if save_kat:
1728
                if kat_name is None:
Daniel Brown's avatar
Daniel Brown committed
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
                    kat_name = "pykat_output"                
                
                cwd = os.path.os.getcwd()
                newkatfile = os.path.join(cwd, kat_name + ".kat")
                
                if os.path.isfile(newkatfile):
                    os.remove(newkatfile)
                  
                os.rename(katfile.name, newkatfile)         
                
1739
                if self.verbose: print ("Kat file saved to '{0}'".format(newkatfile))
Daniel Brown's avatar
Daniel Brown committed
1740
1741
1742
                
            katfile.close()
            perfData = []
1743
1744

            rtn = [r]
Daniel Brown's avatar
Daniel Brown committed
1745
            
Daniel Brown's avatar
Daniel Brown committed
1746
1747
1748
            if sys.version > '3':
                long = int
            
Daniel Brown's avatar
Daniel Brown committed
1749
1750
1751
1752
            if self.__time_code:
                perffile = open(root[0] + ".perf",'r')
                
                for l in perffile.readlines():
1753
                    vals = l.strip().split()
1754
1755
                    perfData.append((vals[0], long(vals[1]), long(vals[2])))
                    #perfData.append((vals[0], float(vals[1]), float(vals[2]), float(vals[3])))
Daniel Brown's avatar
Daniel Brown committed
1756
                    
1757
1758
1759
1760
1761
1762
1763
                rtn.append(perfData)
            
            if getTraceData:
                rtn.append(traceData)
                
            if len(rtn) == 1:
                return rtn[0]
Daniel Brown's avatar
Daniel Brown committed
1764
            else:
1765
                return rtn
1766
1767
        except KeyboardInterrupt as ex:
            print("Keyboard interrupt caught, stopped simulation.")
Daniel Brown's avatar
Daniel Brown committed
1768
        except pkex.FinesseRunError as ex:
1769
1770
1771
1772
1773
            if rethrowExceptions:
                raise ex 
            else:
                pkex.PrintError("Error from Finesse:", ex)
                
Daniel Brown's avatar
Daniel Brown committed
1774
        except pkex.BasePyKatException as ex:
1775
1776
1777
1778
            if rethrowExceptions:
                raise ex 
            else:
                pkex.PrintError("Error from pykat:", ex)
Daniel Brown's avatar
Daniel Brown committed
1779
        finally:
1780
1781
            if self.verbose:
                print ("")
Daniel Brown's avatar
Daniel Brown committed
1782
                print ("Finished in {0:g} seconds".format(float(time.time() - start)))
Daniel Brown's avatar
Daniel Brown committed
1783
            
1784
            
1785
    def remove(self, obj):
Daniel Brown's avatar
Daniel Brown committed
1786
        try:
Daniel Brown's avatar
Daniel Brown committed
1787
            
1788
1789
1790
1791
1792
            if hasattr(obj, "name") and not isinstance(obj, pykat.finesse.Signals) and not (obj.name in self.__components  or obj.name in self.__detectors or obj.name in self.__commands or obj in self.signals.targets):
                raise pkex.BasePyKatException("'{0}' is not currently in the simulation".format(obj.name))
            
            if hasattr(obj, "removed") and obj.removed:
                raise pkex.BasePyKatException("'{0}' has already been removed".format(obj.name))        
Daniel Brown's avatar
Daniel Brown committed
1793

Daniel Brown's avatar
Daniel Brown committed
1794
            nodes = None
1795
        
Daniel Brown's avatar
Daniel Brown committed
1796
1797
1798
1799
1800
1801
1802
1803
            # store nodes that this componet is attached to as a reference for gui
            if isinstance(obj, Component):
                nodes = self.nodes.getComponentNodes(obj)

            if isinstance(obj, Component):    
                del self.__components[obj.name]
                self.__del_component(obj)
                self.nodes._removeComponent(obj)
1804
                
1805
1806
1807
1808
1809
1810
            elif isinstance(obj, Command):  
                if obj._Command__unique:  
                    del self.__commands[obj.__class__.__name__]
                else:
                    del self.__commands[obj.name]
                    
Daniel Brown's avatar
Daniel Brown committed
1811
                self.__del_command(obj)
1812
                
Daniel Brown's avatar
Daniel Brown committed
1813
1814
1815
            elif isinstance(obj, Detector):    
                del self.__detectors[obj.name]
                self.__del_detector(obj)
1816
                
Daniel Brown's avatar
Daniel Brown committed
1817
1818
            elif isinstance(obj, pykat.finesse.Signals):
                obj.remove()
1819
                
Daniel Brown's avatar
Daniel Brown committed
1820
1821
            elif isinstance(obj, pykat.finesse.Signals.fsig):
                obj._on_remove()
Daniel Brown's avatar
Daniel Brown committed
1822
            
Daniel Brown's avatar
Daniel Brown committed
1823
1824
1825
            for b in self.__blocks:
                if obj in self.__blocks[b].contents:
                    self.__blocks[b].contents.remove(obj)
1826
        
Daniel Brown's avatar
Daniel Brown committed
1827
1828
            if self.pykatgui != None:
                self.pykatgui._onComponentRemoved(obj, nodes)
Daniel Brown's avatar
Daniel Brown committed
1829
    
Daniel Brown's avatar
Daniel Brown committed
1830
            del nodes
Daniel Brown's avatar
Daniel Brown committed
1831
        
1832
1833
1834
            if hasattr(obj, "_on_kat_remove"):
                obj._on_kat_remove()
            
Daniel Brown's avatar
Daniel Brown committed
1835
1836
1837
1838
1839
            #import gc
            #print (gc.get_referrers(obj))
            
        except pkex.BasePyKatException as ex:
            pkex.PrintError("Error on removing object:", ex)
1840

1841
    def undumpNodes(self, undumped_name_prefix = "dump"):
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
        """
        Loops through and removes all dump nodes. Required when running quantum noise
        calculations using qnoised as noise must be injected in where losses occur, such as power
        dumped.
        
        The nodes will be renamed 'dump0', 'dump1', ... If by change a kat file already has a
        node called dump0, dump1, etc. then it will skip that name and move on to the next until
        it finds one that doesn't exist.
        """
        
        i = 0
1853
        node_name = "%s_%i" % (str(undumped_name_prefix), i)
1854
1855
1856
1857
    
        for c in self.components.values():
            for n in c.nodes:
                if n.isDump:
1858
                    while hasattr(self.nodes, node_name):
1859
                        node_name = "%s_%i" % (str(undumped_name_prefix), i)
1860
1861
                        i += 1
                        
1862
                    self.nodes.replaceNode(c, n, self.nodes.createNode(node_name))
1863
1864
        
  
1865
1866
1867
1868
1869
1870
1871
1872
1873
    def getMatrices(self):
        
        import scipy
        from scipy.sparse import coo_matrix
        
        prev = self.noxaxis
        
        self.noxaxis = True
        self.printmatrix = True
1874
        print ("".join(self.generateKatScript()))
1875
        self.verbose = True
1876
        self.run()
1877
1878
1879
        self.printmatrix = None
        self.noxaxis = prev        
        
1880
        if self.__prevrunfilename is None:
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
            return None
        else:
            
            Mcarrier = None
            Msignal = None
            
            if os.path.exists("klu_full_matrix_car.dat"):
                M = np.loadtxt("klu_full_matrix_car.dat")
                
                if M.size > 0:
                    row = M[:,0]-1
                    col = M[:,1]-1
                    data = M[:,2] + 1j * M[:,3]
                    N = row.max()+1
                    Mcarrier = coo_matrix((data,(row,col)), shape=(N,N))
                
        
            if os.path.exists("klu_full_matrix_sig.dat"):
                M = np.loadtxt("klu_full_matrix_sig.dat")
                
                if M.size > 0:
                    row = M[:,0]-1
                    col = M[:,1]-1
                    data = M[:,2] + 1j * M[:,3]
                    N = row.max()+1
                    Msignal = coo_matrix((data,(row,col)), shape=(N,N))
        
            return (Mcarrier, Msignal)

1910
    
1911
1912
    def hasNamedObject(self, name):
        return name in self.__components or name in self.__detectors or name in self.__commands
Daniel Brown's avatar
Daniel Brown committed
1913
        
1914
    def add(self, obj, block=NO_BLOCK):
Daniel Brown's avatar
Daniel Brown committed
1915
        try:
1916
1917
            obj.tag = block
            self.__blocks[block].contents.append(obj)
Daniel Brown's avatar
Daniel Brown committed
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
            
            if isinstance(obj, Component):
                
                if obj.name in self.__components :
                    raise pkex.BasePyKatException("A component with name '{0}' has already been added".format([obj.name]))            
                            
                self.__components[obj.name] = obj
                self.__add_component(obj)
                
            elif isinstance(obj, Detector):
                
                if obj.name in self.__detectors :
                        raise pkex.BasePyKatException("A detector '{0}' has already been added".format(obj.name))
                        
                self.__detectors[obj.name] = obj
                self.__add_detector(obj)
                
            elif isinstance(obj, Command):
                
1937
1938
1939
1940
1941
                if obj._Command__unique:
                    self.__commands[obj.__class__.__name__] = obj
                else:
                    self.__commands[obj.name] = obj
                    
Daniel Brown's avatar
Daniel Brown committed
1942
1943
                self.__add_command(obj)
                
1944
            else:
Daniel Brown's avatar
Daniel Brown committed
1945
1946
1947
1948
1949
                raise pkex.BasePyKatException("Object {0} could not be added".format(obj))
                
            obj._on_kat_add(self)
            
        except pkex.BasePyKatException as ex:
Daniel Brown's avatar
Daniel Brown committed
1950
            pkex.PrintError("Error on adding object:", ex)
Daniel Brown's avatar
Daniel Brown committed
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960

    def readOutFile(self, filename):
        
        with open(filename,'r') as outfile:
            # read first to lines to get to header line
            outfile.readline()
            outfile.readline()
            
            hdr = outfile.readline().replace('%','').replace('\n','').split(',')
        
1961
        data = np.loadtxt(filename, comments='%',skiprows=4)
1962
        
1963
1964
1965
        # convert 1D arrays into 2D ones for simpler selection
        if len(data.shape) == 1:
            data = np.array([data])
1966
        
1967
        if hasattr(self, "x2axis") and self.noxaxis == False:
Daniel Brown's avatar
Daniel Brown committed
1968
1969
            # need to parse 2D outputs slightly different as they are effectively 2D matrices
            # written in linear form
1970
            x = data[0::(1+self.x2axis.steps),0].squeeze()
1971
            y = data[0:(1+self.x2axis.steps),1]
Daniel Brown's avatar
Daniel Brown committed
1972
1973
            # get rows and columns lined up so that we can reshape a single column of all x/y data
            # into a matrix
1974
            z = data[:,2:].transpose().reshape(data.shape[1]-2, 1+self.xaxis.steps, 1+self.x2axis.steps).squeeze()
1975
1976
1977
1978
1979
            
            # ensure we have a shape (num outputs, x, y)
            if len(z.shape) == 2:
                z = z.reshape(1, z.shape[0], z.shape[1])
                
Daniel Brown's avatar
Daniel Brown committed
1980
1981
            # once you do this the data for y and x axes need swapping
            z = z.swapaxes(1,2)
1982
            
Daniel Brown's avatar
Daniel Brown committed
1983
1984
1985
1986
            return [x, y, z, hdr]
        else:
            shape_len = len(data.shape)
            
1987
            rows,cols = data.shape
Daniel Brown's avatar
Daniel Brown committed
1988
            
1989
            x = data[:,0].squeeze()
1990
            y = data[:,1:cols]
1991
        
Daniel Brown's avatar
Daniel Brown committed
1992
            return [x, y, hdr]
1993
1994

    def removeLine(self, fragment) :
1995
        """
1996
1997
1998
1999
2000
        This will search all blocks and search for the string
        fragment specified and remove it.
        WARNING: This will only remove non-parsed commands, it will not
        remove commands that have already been parsed
        into a pykat object, such as mirrors and beamsplitters, use
For faster browsing, not all history is shown. View entire blame