예제 #1
0
파일: init.py 프로젝트: mzimny/aamks
class OnEnd():
    def __init__(self):  # {{{
        ''' Stuff that happens at the end of the project '''
        self.json = Json()
        self.conf = self.json.read("{}/conf.json".format(
            os.environ['AAMKS_PROJECT']))
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.project_id = self.conf['project_id']
        self.scenario_id = self.conf['scenario_id']
        self.p = Psql()
        if self.conf['navmesh_debug'] == 1:
            self._navmeshes_for_floors()
        Vis({
            'highlight_geom': None,
            'anim': None,
            'title': "OnEnd()",
            'srv': 1
        })
        self._gearman_register_works()
# }}}

    def _navmeshes_for_floors(self):  # {{{
        navs = {}
        for floor in self.json.readdb('floors_meta').keys():
            z = self.s.query(
                "SELECT name FROM aamks_geom WHERE floor=? AND room_enter='no'",
                (floor, ))
            bypass_rooms = []
            for i in z:
                bypass_rooms.append(i['name'])
            navs[tuple(bypass_rooms)] = Navmesh()
            navs[tuple(bypass_rooms)].build(floor, bypass_rooms)
            navs[tuple(bypass_rooms)].test()
# }}}

    def _gearman_register_works(self):  # {{{
        ''' 
        We only register works. The works will be run by workers registered via
        manager. 
        '''

        if os.environ['AAMKS_USE_GEARMAN'] == '0':
            return

        si = SimIterations(self.project_id, self.scenario_id,
                           self.conf['number_of_simulations'])
        try:
            for i in range(*si.get()):
                worker = "{}/workers/{}".format(os.environ['AAMKS_PROJECT'], i)
                worker = worker.replace("/home", "")
                gearman = "gearman -b -f aRun 'https://www.ctrk.pl:444{}'".format(
                    worker)
                os.system(gearman)
        except Exception as e:
            print('OnEnd: {}'.format(e))
예제 #2
0
class Manager():
    def __init__(self):  # {{{
        ''' This is the manager of aamks jobs via gearman. '''
        try:
            os.remove("/tmp/manage_aamks.sqlite")
        except OSError:
            pass

        self.json = Json()
        self.conf = self.json.read("{}/manager/conf.json".format(
            os.environ['AAMKS_PATH']))
        self._list_enabled_networks()
        self._argparse()
# }}}

    def _gearman_register_results_collector(self):  # {{{
        ''' 
        The worker reports each complete work to aOut service.
        Gearman can then connect to worker machine and download the results.
        '''

        Popen(
            "(echo workers ; sleep 0.1) | netcat {} 4730 | grep -q aOut || {{ gearman -w -h {} -f aOut xargs python3 {}/manager/results_collector.py; }}"
            .format(os.environ['AAMKS_SERVER'], os.environ['AAMKS_SERVER'],
                    os.environ['AAMKS_PATH']),
            shell=True)
# }}}

    def _list_enabled_networks(self):  # {{{
        print("Networks enabled in conf.json")
        dd(self.conf['enabled_networks'])
        print("")
# }}}

    def _access_hosts(self):  # {{{
        self.s = Sqlite("/tmp/manage_aamks.sqlite")
        self._sqlite_import_hosts()
# }}}

    def ping_workers(self):  # {{{
        self._access_hosts()
        for i in self.s.query(
                "SELECT distinct(host),network FROM workers WHERE conf_enabled=1 ORDER BY network,host"
        ):
            Popen(
                '''host={}; net={}; nc -w 2 -z $host 22 2>/dev/null && {{ echo "$host\t($net)\t●"; }} || {{ echo "$host ($net)\t-"; }}'''
                .format(i['host'], i['network']),
                shell=True)
        time.sleep(2)
# }}}

    def wake_on_lan(self):  # {{{
        self._access_hosts()
        for i in self.s.query(
                "SELECT distinct(host),mac,broadcast FROM workers WHERE conf_enabled=1 ORDER BY network,host"
        ):
            Popen("wakeonlan -i {} {}".format(i['broadcast'], i['mac']),
                  shell=True)
        time.sleep(2)

# }}}

    def add_workers(self):  # {{{
        ''' Register each host enabled in conf.json to gearman $AAMKS_SERVER (.bashrc) '''

        self._access_hosts()
        for i in self.s.query(
                "SELECT host FROM workers WHERE conf_enabled=1 ORDER BY network,host"
        ):
            Popen(
                "ssh -f -o ConnectTimeout=3 {} \" nohup gearman -w -h {} -f aRun xargs python3 {}/evac/worker.py > /dev/null 2>&1 &\""
                .format(i['host'], os.environ['AAMKS_SERVER'],
                        os.environ['AAMKS_PATH']),
                shell=True)
            print(
                "ssh -f -o ConnectTimeout=3 {} \" nohup gearman -w -h {} -f aRun xargs python3 {}/evac/worker.py > /dev/null 2>&1 &\""
                .format(i['host'], os.environ['AAMKS_SERVER'],
                        os.environ['AAMKS_PATH']))

        self._gearman_register_results_collector()

# }}}

    def exec_command(self, cmd):  # {{{
        ''' Exec cmd on each host enabled in conf.json '''

        self._access_hosts()
        for i in self.s.query(
                "SELECT distinct(host),network FROM workers WHERE conf_enabled=1 ORDER BY network,host"
        ):
            print(
                '\n..........................................................')
            print(i['host'], "\t\t\t\t", i['network'])
            Popen("ssh -o ConnectTimeout=4 {} \"nohup sudo {} &\"".format(
                i['host'], cmd),
                  shell=True)
            time.sleep(1)
# }}}

    def kill_by_pattern(self, pattern):  # {{{
        ''' Kill process by pattern on each host registered at gearman $AAMKS_SERVER '''

        cmd = "pkill -9 -f '^{}'".format(pattern)
        print(cmd)
        self._access_hosts()
        for i in self.s.query(
                "SELECT distinct(host) FROM workers WHERE gearman_registered=1 ORDER BY network,host"
        ):
            Popen("ssh -o ConnectTimeout=3 {} \"nohup sudo {} &\"".format(
                i['host'], cmd),
                  shell=True)

# }}}

    def update_workers(self):  # {{{
        ''' 
        * update git
        * install packages

        * SGSP specific -- you need to replace it on your own:
            * In SGSP we tag hosts as workers via this check in ~/.bashrc:
              [ -f "/etc/aamks_worker" ] && { export AAMKS_SERVER=192.168.100.131 }
            * In SGSP we use /home/svn/svn_mimooh/config/ for storing .bashrc
        '''

        self._access_hosts()
        for i in self.s.query(
                "SELECT distinct(host) FROM workers WHERE conf_enabled=1 ORDER BY network,host"
        ):
            cmds = []
            cmds.append("ssh -o ConnectTimeout=3 {} ".format(i['host']))
            cmds.append("\"")
            cmds.append("echo ; echo ;")
            cmds.append("echo \`cat /etc/hostname\` ; ")
            cmds.append(
                "svn co https://github.com/aamks/aamks.git/branches/0.2 $AAMKS_PATH; "
            )
            cmds.append("svn up /home/svn/svn_mimooh/configs; ")
            cmds.append(
                "sudo apt-get install --yes python3-pip ipython3 python3-urllib3 gearman sendxmpp; "
            )
            cmds.append("sudo -H pip3 install --upgrade pip ; ")
            cmds.append("sudo -H pip3 install networkX numpy ; ")
            cmds.append("rm -rf ~/.cache/; ")
            cmds.append("sudo touch /etc/aamks_worker; ")
            cmds.append("\"")
            Popen("".join(cmds), shell=True)
            time.sleep(5)

        for i in self.s.query(
                "SELECT distinct(host) FROM workers WHERE conf_enabled=1 ORDER BY network,host"
        ):
            cmds = []
            cmds.append("ssh -o ConnectTimeout=3 {} ".format(i['host']))
            cmds.append("\"")
            cmds.append("echo ; echo ;")
            cmds.append("echo \`cat /etc/hostname\` ; ")
            cmds.append("unset LANG ; ")
            cmds.append("svn info {} | grep 'URL: https' ; ".format(
                os.environ['AAMKS_PATH']))
            cmds.append("svn info {} | grep 'Revision' ; ".format(
                os.environ['AAMKS_PATH']))
            cmds.append(
                "svn info /home/svn/svn_mimooh/configs/ | grep 'Revision' ; ")
            cmds.append("echo AAMKS_SERVER: {} ; ".format(
                os.environ['AAMKS_SERVER']))
            cmds.append("\"")
            Popen("".join(cmds), shell=True)
            time.sleep(1)

# }}}

    def reset_gearmand(self):  # {{{
        cmds = "sudo killall -9 gearman 2>/dev/null; sudo /etc/init.d/gearman-job-server restart"
        Popen(cmds, shell=True)

# }}}

    def list_tasks(self):  # {{{
        ''' Check gearmand status on localhost '''
        Popen(
            '''gearadmin --workers; echo; echo "func\ttasks\tworking\tworkers"; gearadmin --status''',
            shell=True)
        time.sleep(2)

# }}}

    def _sqlite_import_hosts(self):  # {{{
        ''' Create table with all potential hosts in it. 
        If host has has 4 cores enabled, then we will have 4 records in this table
        If host has 0 cores enabled, then we will still have 1 record for it, because host may be disabled just now, but has been gearman_registered previously
        '''

        self.s.query(
            "CREATE TABLE workers('mac','host','broadcast','network','conf_enabled','gearman_registered')"
        )
        for network, cores in self.conf['enabled_networks'].items():
            for record in self.conf['networks'][network]:
                record.append(network)
                if cores == 0:
                    record.append(0)  # conf_enabled=0
                else:
                    record.append(1)  # conf_enabled=1
                record.append(0)  # gearman_registered=0 for now
                self.s.query('INSERT INTO workers VALUES (?,?,?,?,?,?)',
                             record)
                for core in range(1, cores):
                    self.s.query('INSERT INTO workers VALUES (?,?,?,?,?,?)',
                                 record)

        proc = Popen(["gearadmin", "--workers"], stdout=PIPE)
        registered_hosts = []
        for line in proc.stdout:
            x = str(line, 'utf-8').split()
            try:
                self.s.query(
                    'UPDATE workers SET gearman_registered=1 WHERE host=?',
                    (x[1], ))
            except:
                pass
# }}}

    def _argparse(self):  # {{{
        parser = argparse.ArgumentParser(description='options on localhost')

        parser.add_argument('-a',
                            help='add workers from conf.json',
                            required=False,
                            action='store_true')
        parser.add_argument(
            '-c',
            help='exec shell commands on each registered worker',
            required=False)
        parser.add_argument('-k',
                            help='kill all workers',
                            required=False,
                            action='store_true')
        parser.add_argument(
            '-K',
            help='kill processes matching pattern e.g. "gearman.*127.0.0.1"',
            required=False)
        parser.add_argument('-l',
                            help='list tasks',
                            required=False,
                            action='store_true')
        parser.add_argument('-p',
                            help='ping all workers',
                            required=False,
                            action='store_true')
        parser.add_argument('-r',
                            help='reset all gearmand ',
                            required=False,
                            action='store_true')
        parser.add_argument('-U',
                            help='update workers',
                            required=False,
                            action='store_true')
        parser.add_argument('-w',
                            help='wakeOnLan',
                            required=False,
                            action='store_true')
        args = parser.parse_args()

        if args.a:
            self.add_workers()
        if args.c:
            self.exec_command(args.c)
        if args.k:
            self.kill_by_pattern("gearman|^cfast|^python3")
        if args.K:
            self.kill_by_pattern(args.K)
        if args.l:
            self.list_tasks()
        if args.p:
            self.ping_workers()
        if args.r:
            self.reset_gearmand()
        if args.U:
            self.update_workers()
        if args.w:
            self.wake_on_lan()
예제 #3
0
class CfastPartition():
    def __init__(self, verbose=0): # {{{
        ''' 
        Divide space into cells for smoke conditions queries asked by evacuees.
        A cell may be a square or a rectangle. First divide space into squares
        of self._square_side. Iterate over squares and if any square is crossed by an
        obstacle divide this square further into rectangles. 
        
        In the final structure of partition.json we encode each cell.
        Each cell is sorted by x, which allows quick bisections.

        * In each cell we always encode the first sub-cell - the square itself.
        (2000, 2449): OrderedDict([('x', (2000,)), ('y', (2449,))])

        * Then we can add more sub-cells (rectangles).
        (1600, 2449): OrderedDict([('x', (1600, 1600, 1842, 1842)), ('y', (2449, 2541, 2449, 2541))])

        Partition will be later used for filling the cells with smoke
        conditions. Finally we get a tool for quick altering of the state of an
        evacuee at x,y.

        '''

        self._square_side=300
        self.s=Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        try:
            self.s.query("DROP TABLE cell2compa")
            self.s.query("DROP TABLE query_vertices")
        except:
            pass
        self.s.query("CREATE TABLE cell2compa(json)")
        self.s.query("CREATE TABLE query_vertices(json)")

        self.json=Json() 
        self._cell2compa=OrderedDict()
        self._save=OrderedDict()
        floors=json.loads(self.s.query("SELECT * FROM floors")[0]['json'])
        for floor in floors.keys():
            self._init_space(floor) 
            self._intersect_space() 
            self._optimize(floor)
            self._make_cell2compa(floor)
            if(verbose==1):
                self._plot_space(floor)  # debug
        if(verbose==1):
            Vis(None, 'image', 'partition') # debug
        self._dbsave()
# }}}
    def _init_space(self,floor):# {{{
        ''' Divide floor into squares. Prepare empty rectangles placeholders. '''

        floors=json.loads(self.s.query("SELECT * FROM floors")[0]['json'])
        fdims=floors[floor]

        self.squares=OrderedDict()
        self.rectangles=OrderedDict()
        self.lines=[]

        for i in self.s.query("SELECT * FROM aamks_geom WHERE type_pri='COMPA' AND floor=? ORDER BY x0,y0", (floor,)):
            self.lines.append(LineString([ Point(i['x0'],i['y0']), Point(i['x0'], i['y1'])] ))
            self.lines.append(LineString([ Point(i['x0'],i['y0']), Point(i['x1'], i['y0'])] ))
            self.lines.append(LineString([ Point(i['x1'],i['y1']), Point(i['x0'], i['y1'])] ))
            self.lines.append(LineString([ Point(i['x1'],i['y1']), Point(i['x1'], i['y0'])] ))

        x=int(fdims['width']/self._square_side)+1
        y=int(fdims['height']/self._square_side)+1
        for v in range(y):
            for i in range(x):
                x_=fdims['minx']+self._square_side*i
                y_=fdims['miny']+self._square_side*v
                xy=(x_, y_)
                self.squares[xy]=box(x_, y_, x_+self._square_side, y_+self._square_side)
                self.rectangles[xy]=[]
# }}}
    def _candidate_intersection(self,id_,points):# {{{
        ''' 
        So there's an intersection "points" of the square and the walls of the
        room. We get 2 points in this call. Should we create a rectangle of any
        of these points? The rectangle is defined by it's (minX,minY) vertex.
        We only accept the points that belong to the square but don't lie on
        the maxX (right) and maxY (top) edges. 
        '''

        right_limit=id_[0]+self._square_side
        top_limit=id_[1]+self._square_side
        for pt in list(zip(points.xy[0], points.xy[1])):
            if right_limit != pt[0] and top_limit != pt[1]:
                self.rectangles[id_].append((int(pt[0]), int(pt[1])))
# }}}
    def _optimize(self, floor):# {{{
        ''' 
        * self.squares (collection of shapely boxen) is not needed anymore
        * self.rectangles must have duplicates removed and must be sorted by x
        * xy_vectors must be of the form: [ [x0,x1,x2,x3], [y0,y1,y2,y3] ]. 

        self._query_vertices are of the form:

        square        : optimized rectangles 
        (1000 , -1000): OrderedDict([('x' , (1000 , 1100)) , ('y' , (-1000 , -1000))])
        (1400 , -1000): OrderedDict([('x' , (1400 , 1500)) , ('y' , (-1000 , -1000))])
        (1800 , -1000): OrderedDict([('x' , (1800 , ))     , ('y' , (-1000 , ))])
        (2200 , -1000): OrderedDict([('x' , (2200 , ))     , ('y' , (-1000 , ))])
        '''

        del(self.squares)

        for id_,rects in self.rectangles.items():
            rects.append(id_)
            self.rectangles[id_]=list(sorted(set(rects)))

        self._query_vertices=OrderedDict()
        for id_,v in self.rectangles.items():
            self._query_vertices[id_]=OrderedDict()
            xy_vectors=list(zip(*self.rectangles[id_]))
            try:
                self._query_vertices[id_]['x']=xy_vectors[0]
                self._query_vertices[id_]['y']=xy_vectors[1]
            except:
                self._query_vertices[id_]['x']=()
                self._query_vertices[id_]['y']=()

        self._save[floor]=OrderedDict()
        self._save[floor]['square_side']=self._square_side
        self._save[floor]['query_vertices']=OrderedDict()
        for k,v in self._query_vertices.items():
            cell_str="{}x{}".format(k[0], k[1])
            self._save[floor]['query_vertices'][cell_str]=v
# }}}
    def _intersect_space(self):# {{{
        ''' 
        We want to further partition the square into rectangles based on obstacles.
        '''

        for line in self.lines: 
            for id_,square in self.squares.items():
                if square.intersects(line):
                    points=square.intersection(line)
                    if points.length>0:
                        self._candidate_intersection(id_,points)
        
# }}}
    def _plot_space(self,floor):# {{{
        ''' 
        Plots the partition on top of the rooms. 
        '''

        z=self.json.read('{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))

        radius=5
        a=self._square_side
        for k,v in self.rectangles.items():
            z[floor]['rectangles'].append( { "xy": k, "width": a , "depth": a , "strokeColor": "#f80" , "strokeWidth": 2 , "opacity": 0.2 } )
            z[floor]['circles'].append(    { "xy": k, "radius": radius , "fillColor": "#fff", "opacity": 0.3 } )
            z[floor]['texts'].append(      { "xy": k, "content": k, "fontSize": 5, "fillColor":"#f0f", "opacity":0.7 })
            for mm in v:
                z[floor]['circles'].append( { "xy": mm, "radius": radius, "fillColor": "#fff", "opacity": 0.3 } )
                z[floor]['texts'].append(   { "xy": mm, "content": mm, "fontSize": 5, "fillColor":"#f0f", "opacity":0.7 })
        self.json.write(z, '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
# }}}

    def _make_cell2compa_record(self,cell,floor):# {{{
        cell_str="{}x{}".format(cell[0], cell[1])
        try:
            self._cell2compa[floor][cell_str]=self.s.query("SELECT name from aamks_geom WHERE floor=? AND type_pri='COMPA' AND ?>=x0 AND ?>=y0 AND ?<x1 AND ?<y1", (floor, cell[0], cell[1], cell[0], cell[1]))[0]['name']
        except:
            pass
# }}}
    def _make_cell2compa(self,floor):#{{{
        self._cell2compa[floor]=OrderedDict()
        for k,v in self._query_vertices.items():
            self._make_cell2compa_record(k,floor)
            for pt in list(zip(v['x'], v['y'])):
                self._make_cell2compa_record(pt,floor)
#}}}

    def _dbsave(self):# {{{
        self.s.query('INSERT INTO cell2compa VALUES (?)'     , (json.dumps(self._cell2compa),))
        self.s.query('INSERT INTO query_vertices VALUES (?)' , (json.dumps(self._save),))
예제 #4
0
파일: smoke_query.py 프로젝트: mzimny/aamks
class SmokeQuery:
    def __init__(self, floor):
        '''
        * On class init we read cell2compa map and and query_vertices from sqlite.
        * We are getting read_cfast_record(T) calls in say 10s intervals:
          We only store this single T'th record in a dict.
        * We are getting lots of get_conditions((x,y),param) calls after
          read_cfast_record(T) returns the needed CFAST record.

        Sqlite sends: 
            a) cell2compa:
                (1000, 600): R_1

            b) query_vertices:
                (1000, 600): [1000, 1100, 1200], [ 10, 10, 12 ]

        After CFAST produces the conditions at time T, feed _compa_conditions.
        For any evacuee's (x,y) it will be easy to find the square he is in. If
        we have rectangles in our square we use some optimizations to find the
        correct rectangle. Finally fetch the conditions via cell-compa map.
        '''

        self.json = Json()
        try:
            self.s = Sqlite("aamks.sqlite", 1)
        except:
            print("mimooh CFAST fallback, not for production!")
            self.s = Sqlite(
                "{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']), 1)

        self.config = self.json.read('{}/evac/config.json'.format(
            os.environ['AAMKS_PATH']))
        self._sqlite_query_vertices(floor)
        self._sqlite_cell2compa(floor)
        self._init_compa_conditions()
        #print("smoke_query, enable me")
        self._cfast_headers()  # TODO needs to enable!

    def _sqlite_query_vertices(self, floor):  # {{{
        ''' 
        Python has this nice dict[(1,2)], but json cannot handle it. We have
        passed it as dict['1x2'] and now need to bring back from str to
        tuple.
        '''

        son = json.loads(
            self.s.query("SELECT * FROM query_vertices")[0]['json'])
        d = son[floor]
        self._square_side = d['square_side']
        self._query_vertices = OrderedDict()
        for k, v in d['query_vertices'].items():
            z = tuple(int(n) for n in k.split("x"))
            self._query_vertices[z] = v
# }}}

    def _sqlite_cell2compa(self, floor):  # {{{
        son = json.loads(self.s.query("SELECT * FROM cell2compa")[0]['json'])
        d = son[floor]
        self._cell2compa = OrderedDict()
        for k, v in d.items():
            z = tuple(int(n) for n in k.split("x"))
            self._cell2compa[z] = v
# }}}

    def _results(self, query, cell):  # {{{
        ''' Outside is for debugging - should never happen in aamks. '''
        try:
            compa = self._cell2compa[cell]
        except Exception as e:
            compa = "outside"

        return self.compa_conditions[compa], compa
# }}}

    def _init_compa_conditions(self):  # {{{
        ''' 
        Prepare dict structure for cfast csv values. Csv contain some params that are
        relevant for aamks and some that are not.

            self._compa_conditions['R_1']: OrderedDict([('TIME', None), ('CEILT' , None) , ...)
            self._compa_conditions['R_2']: OrderedDict([('TIME', None), ('CEILT' , None) , ...)
            self._compa_conditions['R_3']: OrderedDict([('TIME', None), ('CEILT' , None) , ...)

        self._compa_conditions['outside']=OrderedDict() is more for debugging.
        '''

        self.relevant_params = ('CEILT', 'DJET', 'FLHGT', 'FLOORT', 'HGT',
                                'HRR', 'HRRL', 'HRRU', 'IGN', 'LLCO', 'LLCO2',
                                'LLH2O', 'LLHCL', 'LLHCN', 'LLN2', 'LLO2',
                                'LLOD', 'LLT', 'LLTS', 'LLTUHC', 'LWALLT',
                                'PLUM', 'PRS', 'PYROL', 'TRACE', 'ULCO',
                                'ULCO2', 'ULH2O', 'ULHCL', 'ULHCN', 'ULN2',
                                'ULO2', 'ULOD', 'ULT', 'ULTS', 'ULTUHC',
                                'UWALLT', 'VOL')

        self.all_compas = [
            i['name'] for i in self.s.query(
                "SELECT name FROM aamks_geom where type_pri = 'COMPA'")
        ]

        self.compa_conditions = OrderedDict()
        for compa in self.all_compas:
            self.compa_conditions[compa] = OrderedDict([
                (x, None) for x in ['TIME'] + list(self.relevant_params)
            ])
        self.compa_conditions['outside'] = OrderedDict([('TIME', None)])
# }}}

    def _cfast_headers(self):  # {{{
        '''
        CFAST must have produced the first lines of csv by now, which is the header.
        Get 3 first rows from n,s,w files and make headers: params and geoms.
        Happens only once.
        '''

        self._headers = OrderedDict()
        for letter in ['n', 's', 'w']:
            f = 'cfast_{}.csv'.format(letter)
            with open(f, 'r') as csvfile:
                reader = csv.reader(csvfile, delimiter=',')
                headers = []
                for x in range(3):
                    headers.append(
                        [field.replace(' ', '') for field in next(reader)])
                    headers[x]
                headers[0] = [re.sub("_.*", "", xx) for xx in headers[0]]

            self._headers[letter] = OrderedDict()
            self._headers[letter]['params'] = headers[0]
            self._headers[letter]['geoms'] = headers[2]
# }}}

    def cfast_has_time(self, time):  # {{{
        ''' 
        CFAST dumps 4 header records and then data records. 
        Data records are indexed by time with delta=10s.
        AAMKS has this delta hardcoded: CFAST dumps data in 10s intervals.
        '''

        needed_record_id = int(time / 10) + 1
        with open('cfast_n.csv') as f:
            num_data_records = sum(1 for _ in f) - 4
        if num_data_records > needed_record_id:
            return 1
        else:
            return 0
# }}}

    def read_cfast_record(self, time):  # {{{
        ''' 
        We had parsed headers separately. Now we only parse numbers from n,s,w files. 
        Application needs to call us prior to massive queries for conditions at (x,y).
        '''

        for letter in ['n', 's', 'w']:
            f = 'cfast_{}.csv'.format(letter)
            with open(f, 'r') as csvfile:
                reader = csv.reader(csvfile, delimiter=',')
                for x in range(4):
                    next(reader)
                for row in reader:
                    if int(float(row[0])) == time:
                        needed_record = [float(j) for j in row]
                        needed_record[0] = int(float(row[0]))
                        break

            for compa in self.all_compas:
                self.compa_conditions[compa]['TIME'] = needed_record[0]
            self.compa_conditions['outside']['TIME'] = needed_record[0]

            for m in range(len(needed_record)):
                if self._headers[letter]['params'][
                        m] in self.relevant_params and self._headers[letter][
                            'geoms'][m] in self.all_compas:
                    self.compa_conditions[self._headers[letter]['geoms'][m]][
                        self._headers[letter]['params'][m]] = needed_record[m]
        return 1
# }}}

    def get_conditions(self, q, floor):  # {{{
        ''' 
        First we find to which square our q belongs. If this square has 0 rectangles
        then we return conditions from the square. If the square has rectangles
        we need to loop through those rectangles. Finally we read the smoke
        conditions from the cell. 
        '''

        floors = json.loads(
            self.s.query("SELECT * FROM floors_meta")[0]['json'])
        self.floor_dim = floors[str(floor)]

        x = self.floor_dim['minx'] + self._square_side * int(
            (q[0] - self.floor_dim['minx']) / self._square_side)
        y = self.floor_dim['miny'] + self._square_side * int(
            (q[1] - self.floor_dim['miny']) / self._square_side)

        if len(self._query_vertices[x, y]['x']) == 1:
            return self._results(q, (x, y))
        else:
            for i in range(
                    bisect.bisect(self._query_vertices[(x, y)]['x'], q[0]), 0,
                    -1):
                if self._query_vertices[(x, y)]['y'][i - 1] < q[1]:
                    rx = self._query_vertices[(x, y)]['x'][i - 1]
                    ry = self._query_vertices[(x, y)]['y'][i - 1]
                    return self._results(q, (rx, ry))
        return self._results(q, (x, y))  # outside!
# }}}

    def get_visibility(self, position, time, floor):  # {{{
        query = self.get_conditions(position, floor)
        conditions = query[0]
        room = query[1]
        #print('FLOOR: {}, ROOM: {}, HGT: {}, TIME: {}'.format(floor, room, conditions['HGT'], time))

        if conditions == 'outside':
            print('outside')

        hgt = conditions['HGT']
        if hgt == None:
            return 0, room

        if hgt > self.config['LAYER_HEIGHT']:
            return conditions['LLOD'], room
        else:
            return conditions['ULOD'], room
# }}}

    def get_fed(self, position, time, floor):  # {{{
        conditions = self.get_conditions(position, floor)[0]
        hgt = conditions['HGT']
        if hgt == None:
            return 0.

        if hgt > self.config['LAYER_HEIGHT']:
            layer = 'L'
        else:
            layer = 'U'

        fed_co = 2.764e-5 * ((conditions[layer + 'LCO'] * 1000000)**
                             1.036) * (self.config['TIME_STEP'] / 60)
        fed_hcn = (exp((conditions[layer + 'LHCN'] * 10000) / 43) / 220 -
                   0.0045) * (self.config['TIME_STEP'] / 60)
        fed_hcl = (
            (conditions['LLHCL'] * 1000000) / 1900) * self.config['TIME_STEP']
        fed_o2 = (self.config['TIME_STEP'] /
                  60) / (60 * exp(8.13 - 0.54 *
                                  (20.9 - conditions[layer + 'LO2'])))
        hv_co2 = exp(0.1903 * conditions[layer + 'LCO2'] + 2.0004) / 7.1
        fed_total = (fed_co + fed_hcn + fed_hcl) * hv_co2 + fed_o2

        return fed_total
# }}}

    def get_final_vars(self):  # {{{
        '''
        The app should call us after CFAST produced all output. These are the
        summaries of various min values.
        '''

        self._collect_final_vars()
        #dd(self.sf.query('SELECT * from finals order by param,value'))
        finals = OrderedDict()

        # min(time) for HGT_COR < 1.8
        hgt = self.sf.query(
            "SELECT MIN(time) FROM finals WHERE compa_type='c' AND param='HGT' AND value < 1.8"
        )[0]['MIN(time)']
        if hgt == None:
            hgt = 9999
        ulod = self.sf.query(
            "SELECT MIN(time) FROM finals WHERE compa_type='c' AND param='ULOD' AND value > 0"
        )[0]['MIN(time)']
        if ulod == None:
            ulod = 9999
        ult = self.sf.query(
            "SELECT MIN(time) FROM finals WHERE compa_type='c' AND param='ULT' AND value > 60"
        )[0]['MIN(time)']
        if ult == None:
            ult = 9999
        finals['dcbe'] = max(hgt, ulod, ult)

        # min(HGT_COR)
        finals['min_hgt_cor'] = self.sf.query(
            "SELECT MIN(value) FROM finals WHERE compa_type='c' AND param='HGT'"
        )[0]['MIN(value)']

        # min(HGT_COMPA)
        finals['min_hgt_compa'] = self.sf.query(
            "SELECT MIN(value) FROM finals WHERE param='HGT'")[0]['MIN(value)']

        # min(ULT_COMPA)
        finals['max_temp_compa'] = self.sf.query(
            "SELECT MAX(value) FROM finals WHERE param='ULT'")[0]['MAX(value)']

        c_const = 5
        # min(ULOD_COR)
        ul_od_cor = self.sf.query(
            "SELECT MAX(value) FROM finals WHERE compa_type='c' AND param='ULOD'"
        )[0]['MAX(value)']
        if ul_od_cor == 0:
            finals['min_vis_cor'] = 30
        else:
            finals['min_vis_cor'] = c_const / (ul_od_cor * 2.303)

        # min(ULOD_COMPA)
        ul_od_compa = self.sf.query(
            "SELECT MAX(value) FROM finals WHERE param='ULOD'"
        )[0]['MAX(value)']
        if ul_od_compa == 0:
            finals['min_vis_compa'] = 30
        else:
            finals['min_vis_compa'] = c_const / (ul_od_compa * 2.303)

        return finals

        # }}}
    def _collect_final_vars(self):  # {{{
        ''' 
        Create finals.sqlite for this very sim_id. Convert CFAST csvs into sqlite.
        '''

        finals = []

        for letter in ['n', 's']:
            f = 'cfast_{}.csv'.format(letter)
            with open(f, 'r') as csvfile:
                reader = csv.reader(csvfile, delimiter=',')
                for x in range(4):
                    next(reader)
                for row in reader:
                    record = [float(j) for j in row]
                    record[0] = int(float(row[0]))
                    for i, param in enumerate(self._headers[letter]['params']):
                        if param != 'Time':
                            if self._headers[letter]['geoms'][i] != 'Outside':
                                if self._headers[letter]['geoms'][
                                        i] != 'medium':
                                    compa = self._headers[letter]['geoms'][i][
                                        0]
                                    finals.append(
                                        (record[0], param, record[i],
                                         self._headers[letter]['geoms'][i],
                                         compa))

        try:
            os.remove("finals.sqlite")
        except:
            pass
        self.sf = Sqlite("finals.sqlite")
        self.sf.query(
            "CREATE TABLE finals('time','param','value','compa','compa_type')")
        self.sf.executemany(
            'INSERT INTO finals VALUES ({})'.format(','.join(
                '?' * len(finals[0]))), finals)
예제 #5
0
class World2d():
    ''' World2d is a display of all floors in a single 2d world '''
    def __init__(self):  # {{{
        self.json = Json()
        self.conf = self.json.read("{}/conf.json".format(
            os.environ['AAMKS_PROJECT']))
        self.world_meta = self.json.readdb("world_meta")
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.floors_meta = self.json.readdb("floors_meta")
        self.floors = self.floors_meta.keys()
        self.walls_width = self.world_meta['walls_width']
        self.projections = {'top': dict(), 'side': dict()}
        self._top_projection_make()
        self._top_proj_lines()
        self._meta_translate_y()
        self._world2d_boundaries()
# }}}

    def _top_projection_make(self):  # {{{
        '''
        In world2d we have top and left projections
        '''

        self.projections['top']['padding_rectangle'] = 300
        self.projections['top']['padding_vertical'] = 200
        self.projections['top']['x0'] = self.s.query(
            "SELECT min(x0) AS m FROM aamks_geom"
        )[0]['m'] - self.projections['top']['padding_rectangle']
        self.projections['top']['x1'] = self.s.query(
            "SELECT max(x1) AS m FROM aamks_geom")[0]['m']
# }}}

    def _top_proj_lines(self):  # {{{
        '''
        Helper horizontal lines for Animator: 2, 1, 0
        '''

        lines = OrderedDict()
        absolute = 0
        for floor in list(self.floors_meta.keys())[::-1]:
            if absolute == 0:
                lines[floor] = absolute + self.floors_meta[floor][
                    'ydim'] + self.projections['top']['padding_vertical']
            else:
                lines[floor] = absolute + self.floors_meta[floor][
                    'ydim'] + self.projections['top']['padding_vertical'] * 2
            absolute = lines[floor]
        self.projections['top']['lines'] = lines

# }}}

    def _meta_translate_y(self):  # {{{
        '''
        Calculate translateY (ty). Animator needs this meta info to dynamicaly
        merge floors in world2d view. At some point we will probably introduce
        tx for staircases. 
        '''

        floors_meta = self.json.readdb("floors_meta")

        for floor, line in self.projections['top']['lines'].items():
            self.floors_meta[floor]['ty'] = line - self.projections['top'][
                'padding_vertical'] - self.floors_meta[floor]['maxy']
            self.floors_meta[floor]['tx'] = 0
        self.s.query("UPDATE floors_meta SET json=?",
                     (json.dumps(self.floors_meta), ))

# }}}

    def _world2d_boundaries(self):  # {{{
        m = {}
        m['minx'] = 99999999999
        m['miny'] = 99999999999
        m['maxx'] = -99999999999
        m['maxy'] = -99999999999
        for floor, meta in self.json.readdb("floors_meta").items():
            m['minx'] = min(m['minx'], meta['minx'] + meta['tx'])
            m['miny'] = min(m['miny'], meta['miny'] + meta['ty'])
            m['maxx'] = max(m['maxx'], meta['maxx'] + meta['tx'])
            m['maxy'] = max(m['maxy'], meta['maxy'] + meta['ty'])

        m['xdim'] = m['maxx'] - m['minx']
        m['ydim'] = m['maxy'] - m['miny']
        m['center'] = [
            round(m['minx'] + m['xdim'] / 2),
            round(m['miny'] + m['ydim'] / 2), 0
        ]

        self.world_meta['world2d'] = m
        self.s.query("UPDATE world_meta SET json=?",
                     (json.dumps(self.world_meta), ))
예제 #6
0
class CfastMcarlo():
    def __init__(self):  # {{{
        ''' Generate montecarlo cfast.in. Log what was drawn to psql. '''

        self.json = Json()
        self.hrrpua = 0
        self.alpha = 0
        self.fire_origin = None
        self.conf = self.json.read("{}/conf.json".format(
            os.environ['AAMKS_PROJECT']))
        if self.conf['fire_model'] == 'FDS':
            return
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.p = Psql()
        self._psql_collector = OrderedDict()
        self.s.query(
            "CREATE TABLE fire_origin(name,is_room,x,y,z,floor,sim_id)")

        si = SimIterations(self.conf['project_id'], self.conf['scenario_id'],
                           self.conf['number_of_simulations'])

        for self._sim_id in range(*si.get()):
            seed(self._sim_id)
            self._new_psql_log()
            self._make_cfast()
            self._write()
# }}}

# DISTRIBUTIONS / DRAWS

    def _draw_outdoor_temp(self):  # {{{
        outdoor_temp = round(
            normal(self.conf['outdoor_temperature']['mean'],
                   self.conf['outdoor_temperature']['sd']), 2)
        self._psql_log_variable('outdoort', outdoor_temp)
        return outdoor_temp
# }}}

    def _save_fire_origin(self, fire_origin):  # {{{
        fire_origin.append(self._sim_id)
        self.s.query('INSERT INTO fire_origin VALUES (?,?,?,?,?,?,?)',
                     fire_origin)

        self._psql_log_variable('fireorigname', fire_origin[0])
        self._psql_log_variable('fireorig', fire_origin[1])
# }}}

    def _draw_fire_origin(self):  # {{{
        is_origin_in_room = binomial(1, self.conf['fire_starts_in_a_room'])

        self.all_corridors_and_halls = [
            z['name'] for z in self.s.query(
                "SELECT name FROM aamks_geom WHERE type_pri='COMPA' AND fire_model_ignore!=1 AND type_sec in('COR','HALL') ORDER BY global_type_id"
            )
        ]
        self.all_rooms = [
            z['name'] for z in self.s.query(
                "SELECT name FROM aamks_geom WHERE type_sec='ROOM' ORDER BY global_type_id"
            )
        ]
        fire_origin = []
        if is_origin_in_room == 1 or len(self.all_corridors_and_halls) == 0:
            fire_origin.append(str(choice(self.all_rooms)))
            fire_origin.append('room')
        else:
            fire_origin.append(str(choice(self.all_corridors_and_halls)))
            fire_origin.append('non_room')

        compa = self.s.query("SELECT * FROM aamks_geom WHERE name=?",
                             (fire_origin[0], ))[0]
        x = int(compa['x0'] + compa['width'] / 2.0)
        y = int(compa['y0'] + compa['depth'] / 2.0)
        z = int(compa['height'] * (1 - math.log10(uniform(1, 10))))

        fire_origin += [x, y, z]
        fire_origin += [compa['floor']]
        self._save_fire_origin(fire_origin)

        self.fire_origin = fire_origin

        collect = ('FIRE', compa['global_type_id'],
                   round(compa['width'] / (2.0 * 100), 2),
                   round(compa['depth'] / (2.0 * 100),
                         2), z, 1, 'TIME', '0', '0', '0', '0', 'medium')
        return (','.join(str(i) for i in collect))

# }}}

    def _fire_origin(self):  # {{{
        '''
        Either deterministic fire from Apainter, or probabilistic (_draw_fire_origin()). 
        '''

        if len(self.s.query(
                "SELECT * FROM aamks_geom WHERE type_pri='FIRE'")) > 0:
            z = self.s.query("SELECT * FROM aamks_geom WHERE type_pri='FIRE'")
            i = z[0]
            x = i['center_x']
            y = i['center_y']
            z = i['z1'] - i['z0']
            room = self.s.query(
                "SELECT floor,name,type_sec,global_type_id FROM aamks_geom WHERE floor=? AND type_pri='COMPA' AND fire_model_ignore!=1 AND x0<=? AND y0<=? AND x1>=? AND y1>=?",
                (i['floor'], i['x0'], i['y0'], i['x1'], i['y1']))
            if room[0]['type_sec'] in ('COR', 'HALL'):
                fire_origin = [
                    room[0]['name'], 'non_room', x, y, z, room[0]['floor']
                ]
            else:
                fire_origin = [
                    room[0]['name'], 'room', x, y, z, room[0]['floor']
                ]
            self._save_fire_origin(fire_origin)
            collect = ('FIRE', room[0]['global_type_id'], x * 100, y * 100,
                       z * 100, 1, 'TIME', '0', '0', '0', '0', 'medium')
            cfast_fire = (','.join(str(i) for i in collect))
        else:
            cfast_fire = self._draw_fire_origin()

        return cfast_fire
# }}}

    def _draw_fire_properties(self, intervals):  # {{{
        i = OrderedDict()
        i['coyield'] = round(uniform(0.01, 0.043), 3)
        i['sootyield'] = round(uniform(0.11, 0.17), 3)
        i['trace'] = 0
        i['q_star'] = round(uniform(0.5, 2), 3)
        i['heigh'] = 0
        i['radfrac'] = round(gamma(124.48, 0.00217), 3)
        i['heatcom'] = round(uniform(16400000, 27000000), 1)
        for k, v in i.items():
            self._psql_log_variable(k, v)

        result = OrderedDict()
        result['sootyield'] = 'SOOT,{}'.format(','.join([str(i['sootyield'])] *
                                                        intervals))
        result['coyield'] = 'CO,{}'.format(','.join([str(i['coyield'])] *
                                                    intervals))
        result['trace'] = 'TRACE,{}'.format(','.join([str(i['trace'])] *
                                                     intervals))
        result['q_star'] = i['q_star']
        result['heigh'] = 'HEIGH,{}'.format(','.join([str(i['heigh'])] *
                                                     intervals))
        result['radfrac'] = str(i['radfrac'])
        result['heatcom'] = str(i['heatcom'])
        return result
# }}}

    def _draw_fire_development(self):  # {{{
        ''' 
        Generate fire. Alpha t square on the left, then constant in the
        middle, then fading on the right. At the end read hrrs at given times.
        '''

        hrrpua_d = self.conf['hrrpua']
        hrr_alpha = self.conf['hrr_alpha']
        '''
        Fire area is draw from pareto distrubution regarding the BS PD-7974-7. 
        There is lack of vent condition - underventilated fires
        '''
        p = pareto(b=0.668, scale=0.775)
        fire_area = p.rvs(size=1)[0]
        fire_origin = self.fire_origin
        orig_area = self.s.query(
            "SELECT (width * depth)/10000 as area FROM aamks_geom WHERE name='{}'"
            .format(fire_origin[0]))[0]['area']

        if fire_area > orig_area:
            fire_area = orig_area

        self.hrrpua = int(
            triangular(hrrpua_d['min'], hrrpua_d['mode'], hrrpua_d['max']))
        hrr_peak = int(self.hrrpua * 1000 * fire_area)
        self.alpha = int(
            triangular(hrr_alpha['min'], hrr_alpha['mode'], hrr_alpha['max']) *
            1000)

        self._psql_log_variable('hrrpeak', hrr_peak / 1000)
        self._psql_log_variable('alpha', self.alpha / 1000.0)

        # left
        t_up_to_hrr_peak = int((hrr_peak / self.alpha)**0.5)
        interval = int(round(t_up_to_hrr_peak / 10))
        times0 = list(range(0, t_up_to_hrr_peak,
                            interval)) + [t_up_to_hrr_peak]
        hrrs0 = [int((self.alpha * t**2)) for t in times0]

        # middle
        t_up_to_starts_dropping = 15 * 60
        times1 = [t_up_to_starts_dropping]
        hrrs1 = [hrr_peak]

        # right
        t_up_to_drops_to_zero = t_up_to_starts_dropping + t_up_to_hrr_peak
        interval = int(
            round((t_up_to_drops_to_zero - t_up_to_starts_dropping) / 10))
        times2 = list(
            range(t_up_to_starts_dropping, t_up_to_drops_to_zero,
                  interval)) + [t_up_to_drops_to_zero]
        hrrs2 = [
            int((self.alpha * (t - t_up_to_drops_to_zero)**2)) for t in times2
        ]

        times = list(times0 + times1 + times2)
        hrrs = list(hrrs0 + hrrs1 + hrrs2)

        return times, hrrs

# }}}

    def _draw_window_opening(self, outdoor_temp):  # {{{
        ''' 
        Windows are open / close based on outside temperatures but even
        still, there's a distribution of users willing to open/close the
        windows. Windows can be full-open (1), quarter-open (0.25) or closed
        (0). 
        '''

        draw_value = uniform(0, 1)
        for i in self.conf['windows']:
            if outdoor_temp > i['min'] and outdoor_temp <= i['max']:
                if draw_value < i['full']:
                    how_much_open = 1
                elif draw_value < i['full'] + i['quarter']:
                    how_much_open = 0.25
                else:
                    how_much_open = 0
        self._psql_log_variable('w', how_much_open)
        return how_much_open

# }}}

    def _draw_door_and_hole_opening(self, Type):  # {{{
        ''' 
        Door may be open or closed, but Hole is always open and we don't need
        to log that.
        '''
        vents = self.conf['vents_open']

        if Type == 'HOLE':
            how_much_open = 1
        else:
            how_much_open = binomial(1, vents[Type])
            self._psql_log_variable(Type.lower(), how_much_open)

        return how_much_open
# }}}

    def _draw_heat_detectors_triggers(self):  # {{{
        mean = self.conf['heat_detectors']['temp_mean']
        sd = self.conf['heat_detectors']['temp_sd']
        zero_or_one = binomial(1, self.conf['heat_detectors']['not_broken'])
        chosen = round(normal(mean, sd), 2) * zero_or_one
        self._psql_log_variable('heat_detectors', chosen)
        return str(chosen)
# }}}

    def _draw_smoke_detectors_triggers(self):  # {{{
        mean = self.conf['smoke_detectors']['temp_mean']
        sd = self.conf['smoke_detectors']['temp_sd']
        zero_or_one = binomial(1, self.conf['smoke_detectors']['not_broken'])
        chosen = round(normal(mean, sd), 2) * zero_or_one
        self._psql_log_variable('smoke_detectors', chosen)
        return str(chosen)
# }}}

    def _draw_sprinklers_triggers(self):  # {{{
        mean = self.conf['sprinklers']['temp_mean']
        sd = self.conf['sprinklers']['temp_sd']
        zero_or_one = binomial(1, self.conf['sprinklers']['not_broken'])
        chosen = round(normal(mean, sd), 2) * zero_or_one
        self._psql_log_variable('sprinklers', chosen)
        return str(chosen)
# }}}

    def _psql_log_variable(self, attr, val):  #{{{
        self._psql_collector[self._sim_id][attr].append(val)
# }}}

# CFAST SECTIONS

    def _make_cfast(self):  # {{{
        ''' Compose cfast.in sections '''

        outdoor_temp = self._draw_outdoor_temp()
        txt = (
            self._section_preamble(outdoor_temp),
            self._section_matl(),
            self._section_compa(),
            self._section_halls_onez(),
            self._section_windows(outdoor_temp),
            self._section_doors_and_holes(),
            self._section_vvent(),
            self._section_mvent(),
            self._section_fire(),
            self._section_heat_detectors(),
            self._section_smoke_detectors(),
            self._section_sprinklers(),
        )

        with open(
                "{}/workers/{}/cfast.in".format(os.environ['AAMKS_PROJECT'],
                                                self._sim_id), "w") as output:
            output.write("\n".join(filter(None, txt)))
# }}}

    def _section_preamble(self, outdoor_temp):  # {{{
        ''' 
        We use 600 as time, since Cfast will be killed by aamks. 
        '''

        txt = (
            'VERSN,7,{}_{}'.format('SIM', self.conf['project_id']),
            'TIMES,{},-120,10,10'.format(self.conf['simulation_time']),
            'EAMB,{},101300,0'.format(273 + outdoor_temp),
            'TAMB,293.15,101300,0,50',
            'DTCHECK,1.E-9,100',
            '',
        )
        return "\n".join(txt)

# }}}

    def _section_matl(self):  # {{{
        txt = (
            '!! MATL,name,param1,param2,param3,param4,param5,param6',
            'MATL,concrete,1.7,840,2500,0.4,0.9,concrete',
            'MATL,gypsum,0.3,1000,1000,0.02,0.85,gipsum',
            'MATL,glass,0.8,840,2500,0.013,0.9,glass',
            'MATL,block,0.3,840,800,0.03,0.85,floor',
            'MATL,brick,0.3,840,800,0.03,0.85,brick',
            '',
        )
        return "\n".join(txt)
# }}}

    def _section_compa(self):  # {{{
        txt = [
            '!! COMPA,name,width,depth,height,x,y,z,matl_ceiling,matl_floor,matl_wall'
        ]
        for v in self.s.query(
                "SELECT * from aamks_geom WHERE type_pri='COMPA' AND fire_model_ignore!=1 ORDER BY global_type_id"
        ):
            collect = []
            collect.append('COMPA')  # COMPA
            collect.append(v['name'])  # NAME
            collect.append(round(v['width'] / 100.0, 2))  # WIDTH
            collect.append(round(v['depth'] / 100.0, 2))  # DEPTH
            collect.append(round(v['height'] / 100.0, 2))  # INTERNAL_HEIGHT
            collect.append(round(v['x0'] / 100.0, 2))  # ABSOLUTE_X_POSITION
            collect.append(round(v['y0'] / 100.0, 2))  # ABSOLUTE_Y_POSITION
            collect.append(round(v['z0'] / 100.0, 2))  # ABSOLUTE_Z_POSITION
            collect.append(v['material_ceiling'])  # CEILING_MATERIAL_NAME
            collect.append(v['material_floor'])  # FLOOR_MATERIAL_NAME
            collect.append(v['material_wall'])  # WALL_MATERIAL_NAME
            txt.append(','.join(str(i) for i in collect))

        return "\n".join(txt) + "\n" if len(txt) > 1 else ""
# }}}

    def _section_halls_onez(self):  # {{{
        txt = ['!! ONEZ,id']
        for v in self.s.query(
                "SELECT * from aamks_geom WHERE type_sec in ('STAI', 'COR') AND fire_model_ignore!=1 order by type_sec"
        ):

            collect = []
            if v['type_sec'] == 'COR':
                collect.append('HALL')
            else:
                collect.append('ONEZ')
            collect.append(v['global_type_id'])
            txt.append(','.join(str(i) for i in collect))

        return "\n".join(txt) + "\n" if len(txt) > 1 else ""
# }}}

    def _section_windows(self, outdoor_temp):  # {{{
        ''' Randomize how windows are opened/closed. '''
        txt = ['!! WINDOWS, from,to,id,width,soffit,sill,offset,face,open']
        windows_setup = []
        for v in self.s.query(
                "SELECT * FROM aamks_geom WHERE type_tri='WIN' ORDER BY vent_from,vent_to"
        ):
            collect = []
            collect.append('HVENT')  # HVENT
            collect.append(v['vent_from'])  # COMPARTMENT1
            collect.append(v['vent_to'])  # COMPARTMENT2
            collect.append(v['hvent_room_seq'])  # HVENT_NUMBER
            collect.append(round(v['cfast_width'] / 100.0, 2))  # WIDTH
            collect.append(round(
                (v['sill'] + v['height']) / 100.0, 2
            ))  # SOFFIT (height of the top of the hvent relative to the floor)
            collect.append(round(v['sill'] / 100.0, 2))  # SILL
            collect.append(round(v['face_offset'] / 100.0,
                                 2))  # COMPARTMENT1_OFFSET
            collect.append(v['face'])  # FACE
            how_much_open = self._draw_window_opening(outdoor_temp)
            windows_setup.append((how_much_open, v['name']))
            collect.append(how_much_open)
            txt.append(','.join(str(i) for i in collect))

        self.s.executemany(
            'UPDATE aamks_geom SET how_much_open=? WHERE name=?',
            windows_setup)

        return "\n".join(txt) + "\n" if len(txt) > 1 else ""
# }}}

    def _section_doors_and_holes(self):  # {{{
        ''' Randomize how doors are opened/close. '''

        txt = ['!! DOORS, from,to,id,width,soffit,sill,offset,face,open']
        hvents_setup = []
        for v in self.s.query(
                "SELECT * FROM aamks_geom WHERE type_tri='DOOR' ORDER BY vent_from,vent_to"
        ):
            collect = []
            collect.append('HVENT')  # HVENT
            collect.append(v['vent_from'])  # COMPARTMENT1
            collect.append(v['vent_to'])  # COMPARTMENT2
            collect.append(v['hvent_room_seq'])  # VENT_NUMBER
            collect.append(round(v['cfast_width'] / 100.0, 2))  # WIDTH
            collect.append(round(
                (v['sill'] + v['height']) / 100.0, 2
            ))  # SOFFIT (height of the top of the hvent relative to the floor)
            collect.append(round(v['sill'] / 100.0, 2))  # SILL
            collect.append(round(v['face_offset'] / 100.0,
                                 2))  # COMPARTMENT1_OFFSET
            collect.append(v['face'])  # FACE
            how_much_open = self._draw_door_and_hole_opening(
                v['type_sec'])  # HOLE_CLOSE
            hvents_setup.append((how_much_open, v['name']))
            collect.append(how_much_open)
            txt.append(','.join(str(i) for i in collect))

        self.s.executemany(
            'UPDATE aamks_geom SET how_much_open=? WHERE name=?', hvents_setup)

        return "\n".join(txt) + "\n" if len(txt) > 1 else ""
# }}}

    def _section_vvent(self):  # {{{
        # VVENT AREA, SHAPE, INITIAL_FRACTION
        txt = [
            '!! VVENT,top,bottom,id,area,shape,rel_type,criterion,target,i_time, i_frac, f_time, f_frac, offset_x, offset_y'
        ]
        #for v in self.s.query("SELECT distinct v.room_area, v.type_sec, v.vent_from, v.vent_to, v.vvent_room_seq, v.width, v.depth, (v.x0 - c.x0) + 0.5*v.width as x0, (v.y0 - c.y0) + 0.5*v.depth as y0 FROM aamks_geom v JOIN aamks_geom c on v.vent_to_name = c.name WHERE v.type_pri='VVENTS' AND c.type_pri = 'COMPA' ORDER BY v.vent_from,v.vent_to"):
        for v in self.s.query(
                "SELECT distinct v.room_area, v.type_sec, v.vent_from, v.vent_to, v.vvent_room_seq, v.width, v.depth, (v.x0 - c.x0) + 0.5*v.width as x0, (v.y0 - c.y0) + 0.5*v.depth as y0 FROM aamks_geom v JOIN aamks_geom c on v.vent_to_name = c.name WHERE v.type_sec='VVENT' ORDER BY v.vent_from,v.vent_to"
        ):
            collect = []
            collect.append('VVENT')  # VVENT AREA, SHAPE, INITIAL_FRACTION
            collect.append(v['vent_from'])  # COMPARTMENT1
            collect.append(v['vent_to'])  # COMPARTMENT2
            collect.append(v['vvent_room_seq'])  # VENT_NUMBER
            collect.append(
                round((v['width'] * v['depth']) / 1e4, 2)
            )  # AREA OF THE ROOM, feb.2018: previously: round((v['width']*v['depth'])/1e4, 2)
            collect.append(2)  # Type of dumper 1 - round, 2 - squere
            collect.append('TIME')  # Type of realease
            collect.append('')  # empty for time release
            collect.append('')  # empty for time release
            collect.append(60)  # start work on time
            collect.append(0)  # intial state before triggering
            collect.append(120)  # end work on time
            #collect.append(1)           # end state with probability of working
            collect.append(self._draw_door_and_hole_opening(
                v['type_sec']))  # end state with probability of working
            collect.append(round(v['x0'] / 100, 2))  # Offset_x
            collect.append(round(v['y0'] / 100, 2))  # Offset_y

            txt.append(','.join(str(i) for i in collect))

        return "\n".join(txt) + "\n" if len(txt) > 1 else ""
# }}}

    def _section_mvent(self):  # {{{
        # VVENT AREA, SHAPE, INITIAL_FRACTION
        txt = [
            '!!VVENT,first_comp,second_comp,id,orientation1,height_in,area_in,orientation2,height_out,area_out,flowm3/s,press_l,press_u,release,nix,nix,initial_time,initial_fraction,final_time,final_fraction'
        ]
        collect = []
        #collect.append('MVENT,28,35,1,V,2.3,0.48,H,3,0.48,1.7,200,300,TIME,,,60,0,70,1,1,1')
        #collect.append('MVENT,28,35,2,V,2.3,0.48,H,3,0.48,1.7,200,300,TIME,,,60,0,70,1,2.5,1')
        #collect.append('MVENT,35,28,3,V,2.3,0.48,H,3,0.48,1.7,200,300,TIME,,,60,0,70,1,10,1')
        #collect.append('MVENT,35,28,4,V,2.3,0.48,H,3,0.48,1.7,200,300,TIME,,,60,0,70,1,11,1')
        #collect.append('MVENT,30,35,1,V,2.3,0.48,H,3,0.48,1.7,200,300,TIME,,,60,0,70,1,1,1')
        #collect.append('MVENT,31,35,2,V,2.3,0.48,H,3,0.48,1.7,200,300,TIME,,,60,0,70,1,1,1')
        #collect.append('MVENT,35,30,3,V,2.3,0.48,H,3,0.48,1.7,200,300,TIME,,,60,0,70,1,1,4')
        #collect.append('MVENT,35,31,4,V,2.3,0.48,H,3,0.48,1.7,200,300,TIME,,,60,0,70,1,2,1')
        txt.append('\n'.join(str(i) for i in collect))

        return "\n".join(txt) + "\n" if len(txt) > 1 else ""
# }}}

    def _section_heat_detectors(self):  # {{{
        txt = [
            '!! DETECTORS,type,compa,temp,width,depth,height,rti,supress,density'
        ]
        for v in self.s.query(
                "SELECT * from aamks_geom WHERE type_pri='COMPA' AND fire_model_ignore!=1 AND heat_detectors=1"
        ):
            temp = self._draw_heat_detectors_triggers(
            )  # ACTIVATION_TEMPERATURE,
            if temp == '0.0':
                collect = []
            else:
                collect = []
                collect.append('DETECT')  # DETECT,
                collect.append('HEAT')  # TYPE: HEAT,SMOKE,SPRINKLER
                collect.append(v['global_type_id'])  # COMPARTMENT,
                collect.append(temp)  # ACTIVATION_TEMPERATURE,
                collect.append(round(v['width'] / (2.0 * 100), 2))  # WIDTH
                collect.append(round(v['depth'] / (2.0 * 100), 2))  # DEPTH
                collect.append(round(v['height'] / 100.0, 2))  # HEIGHT
                collect.append(80)  # RTI,
                collect.append(0)  # SUPPRESSION,
                collect.append(7E-05)  # SPRAY_DENSITY
                txt.append(','.join(str(i) for i in collect))

        return "\n".join(txt) + "\n" if len(txt) > 1 else ""

# }}}

    def _section_smoke_detectors(self):  # {{{
        txt = [
            '!! DETECTORS,type,compa,temp,width,depth,height,rti,supress,density'
        ]
        for v in self.s.query(
                "SELECT * from aamks_geom WHERE type_pri='COMPA' AND fire_model_ignore!=1 AND smoke_detectors=1"
        ):
            temp = self._draw_smoke_detectors_triggers(
            )  # ACTIVATION_TEMPERATURE,
            if temp == '0.0':
                collect = []
            else:
                collect = []
                collect.append('DETECT')  # DETECT,
                collect.append('SMOKE')  # TYPE: HEAT,SMOKE,SPRINKLER
                collect.append(v['global_type_id'])  # COMPARTMENT,
                collect.append(temp)  # ACTIVATION_TEMPERATURE,
                collect.append(round(v['width'] / (2.0 * 100), 2))  # WIDTH
                collect.append(round(v['depth'] / (2.0 * 100), 2))  # DEPTH
                collect.append(round(v['height'] / 100.0, 2))  # HEIGHT
                collect.append(80)  # RTI,
                collect.append(0)  # SUPPRESSION,
                collect.append(7E-05)  # SPRAY_DENSITY
                txt.append(','.join(str(i) for i in collect))

        return "\n".join(txt) + "\n" if len(txt) > 1 else ""

# }}}

    def _section_sprinklers(self):  # {{{
        txt = [
            '!! SPRINKLERS,type,compa,temp,width,depth,height,rti,supress,density'
        ]
        for v in self.s.query(
                "SELECT * from aamks_geom WHERE type_pri='COMPA' AND fire_model_ignore!=1 AND sprinklers=1"
        ):
            temp = self._draw_sprinklers_triggers()  # ACTIVATION_TEMPERATURE,
            if temp == '0.0':
                collect = []
            else:
                collect = []
                collect.append('DETECT')  # DETECT,
                collect.append('SPRINKLER')  # TYPE: HEAT,SMOKE,SPRINKLER
                collect.append(v['global_type_id'])  # COMPARTMENT,
                collect.append(temp)  # ACTIVATION_TEMPERATURE,
                collect.append(round(v['width'] / (2.0 * 100), 2))  # WIDTH
                collect.append(round(v['depth'] / (2.0 * 100), 2))  # DEPTH
                collect.append(round(v['height'] / 100.0, 2))  # HEIGHT
                collect.append(50)  # RTI,
                collect.append(1)  # SUPPRESSION,
                collect.append(7E-05)  # SPRAY_DENSITY
                txt.append(','.join(str(i) for i in collect))

        return "\n".join(txt) + "\n" if len(txt) > 1 else ""
# }}}

    def _section_fire(self):  # {{{
        fire_origin = self._fire_origin()
        times, hrrs = self._draw_fire_development()
        fire_properties = self._draw_fire_properties(len(times))
        self._fire_obstacle()
        area = nround(npa(hrrs) / (self.hrrpua * 1000) + 0.1, decimals=1)
        fire_origin = self._fire_origin()

        txt = (
            '!! FIRE,compa,x,y,z,fire_number,ignition_type,ignition_criterion,ignition_target,?,?,name',
            fire_origin,
            '',
            '!! CHEMI,?,?,?,?',
            'CHEMI,1,1.8,0.3,0.05,0,0.283,{}'.format(
                fire_properties['heatcom']),
            '',
            'TIME,' + ','.join(str(i) for i in times),
            'HRR,' + ','.join(str(round(i, 3)) for i in hrrs),
            fire_properties['sootyield'],
            fire_properties['coyield'],
            fire_properties['trace'],
            'AREA,' + ','.join(str(i) for i in area),
            fire_properties['heigh'],
        )
        return "\n".join(txt) + "\n"

# }}}

    def _new_psql_log(self):  #{{{
        ''' Init the collector for storing montecarlo cfast setup. Variables will be registered later one at a time. '''
        self._psql_collector[self._sim_id] = OrderedDict([
            ('fireorig', []),
            ('fireorigname', []),
            ('heat_detectors', []),
            ('smoke_detectors', []),
            ('hrrpeak', []),
            ('sootyield', []),
            ('coyield', []),
            ('alpha', []),
            ('trace', []),
            ('area', []),
            ('q_star', []),
            ('heigh', []),
            ('w', []),
            ('outdoort', []),
            ('dcloser', []),
            ('door', []),
            ('sprinklers', []),
            ('heatcom', []),
            ('delectr', []),
            ('vvent', []),
            ('radfrac', []),
        ])
#}}}

    def _write(self):  #{{{
        ''' 
        Write cfast variables to postgres. Both column names and the values for
        psql come from trusted source, so there should be no security issues
        with just joining dict data (non-parametrized query). 
        '''

        pairs = []
        for k, v in self._psql_collector[self._sim_id].items():
            pairs.append("{}='{}'".format(k, ','.join(str(x) for x in v)))
        data = ', '.join(pairs)
        self.p.query(
            "UPDATE simulations SET {} WHERE project=%s AND scenario_id=%s AND iteration=%s"
            .format(data),
            (self.conf['project_id'], self.conf['scenario_id'], self._sim_id))
예제 #7
0
class FDSimporter():
    def __init__(self):  # {{{
        self.json = Json()
        self.conf = self.json.read("{}/conf.json".format(
            os.environ['AAMKS_PROJECT']))
        if self.conf['fire_model'] == 'CFAST':
            return
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.cadfds = self.json.read("{}/cadfds.json".format(
            os.environ['AAMKS_PROJECT']))
        self.doors_width = 32
        self.walls_width = 4
        self._geometry2sqlite()
        self.geomsMap = self.json.read("{}/inc.json".format(
            os.environ['AAMKS_PATH']))['aamksGeomsMap']
        self._floors_meta()
        self._world_meta()
        self._fire_origin()
        self._debug()
# }}}

    def _floors_meta(self):  # {{{
        ''' 
        Floor dimensions are needed here and there. 
        for z_absolute high towers are not taken under account, we want the natural floor's zdim 
        for z_relative high towers are not taken under account, we want the natural floor's zdim 
        '''

        self.floors = [
            z['floor'] for z in self.s.query(
                "SELECT DISTINCT floor FROM aamks_geom ORDER BY floor")
        ]
        self.floors_meta = OrderedDict()
        self._world3d = dict()
        self._world3d['minx'] = 9999999
        self._world3d['maxx'] = -9999999
        self._world3d['miny'] = 9999999
        self._world3d['maxy'] = -9999999
        prev_maxz = 0
        for floor in self.floors:
            ty = 0
            tx = 0
            minx = self.s.query(
                "SELECT min(x0) AS minx FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['minx']
            maxx = self.s.query(
                "SELECT max(x1) AS maxx FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['maxx']
            miny = self.s.query(
                "SELECT min(y0) AS miny FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['miny']
            maxy = self.s.query(
                "SELECT max(y1) AS maxy FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['maxy']
            minz_abs = self.s.query(
                "SELECT min(z0) AS minz FROM aamks_geom WHERE type_sec NOT IN('STAI','HALL') AND floor=?",
                (floor, ))[0]['minz']
            maxz_abs = self.s.query(
                "SELECT max(z1) AS maxz FROM aamks_geom WHERE type_sec NOT IN('STAI','HALL') AND floor=?",
                (floor, ))[0]['maxz']
            zdim = maxz_abs - prev_maxz
            prev_maxz = maxz_abs

            xdim = maxx - minx
            ydim = maxy - miny
            center = (minx + int(xdim / 2), miny + int(ydim / 2), minz_abs)
            self.floors_meta[floor] = OrderedDict([('name', floor),
                                                   ('xdim', xdim),
                                                   ('ydim', ydim),
                                                   ('center', center),
                                                   ('minx', minx),
                                                   ('miny', miny),
                                                   ('maxx', maxx),
                                                   ('maxy', maxy),
                                                   ('minz_abs', minz_abs),
                                                   ('maxz_abs', maxz_abs),
                                                   ('zdim', zdim), ('ty', ty),
                                                   ('tx', tx)])

            self._world3d['minx'] = min(self._world3d['minx'], minx)
            self._world3d['maxx'] = max(self._world3d['maxx'], maxx)
            self._world3d['miny'] = min(self._world3d['miny'], miny)
            self._world3d['maxy'] = max(self._world3d['maxy'], maxy)

        self.s.query("CREATE TABLE floors_meta(json)")
        self.s.query('INSERT INTO floors_meta VALUES (?)',
                     (json.dumps(self.floors_meta), ))

# }}}

    def _world_meta(self):  # {{{
        self.s.query("CREATE TABLE world_meta(json)")
        self.world_meta = {}
        self.world_meta['world3d'] = self._world3d
        self.world_meta['walls_width'] = self.walls_width
        self.world_meta['doors_width'] = self.doors_width

        if len(self.floors_meta) > 1:
            self.world_meta['multifloor_building'] = 1
        else:
            self.world_meta['multifloor_building'] = 0

        self.s.query('INSERT INTO world_meta(json) VALUES (?)',
                     (json.dumps(self.world_meta), ))

# }}}

    def _geometry2sqlite(self):  # {{{
        data = []
        for floor, gg in self.cadfds.items():
            for k, arr in gg.items():
                if k in ('META'):
                    continue
                for v in arr:
                    zz = list(zip(*v['points']))
                    bbox = [
                        int(min(zz[0])),
                        int(min(zz[1])),
                        int(max(zz[0])),
                        int(max(zz[1]))
                    ]
                    attrs = self._prepare_attrs(v)
                    record = self._prepare_geom_record(k, v, bbox, floor,
                                                       attrs, gg['META'])
                    if record != False:
                        data.append(record)
        self.s.query(
            "CREATE TABLE aamks_geom(name,floor,type_pri,type_sec,type_tri,x0,y0,z0,x1,y1,z1,global_type_id,exit_type, room_enter, terminal_door, points)"
        )
        self.s.executemany(
            'INSERT INTO aamks_geom VALUES ({})'.format(','.join(
                '?' * len(data[0]))), data)
#}}}

    def _prepare_attrs(self, v):  # {{{
        aa = {"exit_type": None, "room_enter": None}
        if 'attribs' in v:
            for kk, vv in v['attribs'].items():
                aa[kk] = vv
        return aa
# }}}

    def _prepare_geom_record(self, k, v, bbox, floor, attrs, meta):  # {{{
        ''' Format a record for sqlite. Hvents get fixed width self.doors_width cm '''
        # OBST
        if k in ('OBST', ):
            if len(v['points']) > 5:
                print(
                    "You need to split obstacles having more than 5 vertices")
                exit()
            type_pri = 'OBST'
            type_tri = ''

        # EVACUEE
        elif k in ('EVACUEE', ):
            type_pri = 'EVACUEE'
            type_tri = ''

        # FIRE
        elif k in ('FIRE', ):
            type_pri = 'FIRE'
            type_tri = ''

        # MVENT
        elif k in ('MVENT', ):
            type_pri = 'MVENT'
            type_tri = ''

        # VVENT
        elif k in ('VVENT', ):
            type_pri = 'VVENT'
            height = 10
            type_tri = ''

        # COMPA
        elif k in ('ROOM', 'COR', 'HALL', 'STAI'):
            type_pri = 'COMPA'
            type_tri = ''

        # HVENT
        elif k in ('DOOR', 'DCLOSER', 'DELECTR', 'HOLE', 'WIN'):
            type_pri = 'HVENT'
            if k in ('DOOR', 'DCLOSER', 'DELECTR', 'HOLE'):
                type_tri = 'DOOR'
            elif k in ('WIN'):
                type_tri = 'WIN'

        if 'name' not in v:
            v['name'] = ''

        #self.s.query("CREATE TABLE aamks_geom(name , floor , type_pri , type_sec , type_tri , x0      , y0      , z0         , x1      , y1      , z1         , global_type_id , exit_type          , room_enter          , terminal_door      , points)")
        return (v['name'], floor, type_pri, k, type_tri, bbox[0], bbox[1],
                meta['z0'], bbox[2], bbox[3], meta['z1'], None,
                attrs['exit_type'], attrs['room_enter'], attrs['exit_type'],
                json.dumps(v['points']))

# }}}

    def _fire_origin(self):  # {{{
        si = SimIterations(self.conf['project_id'], self.conf['scenario_id'],
                           self.conf['number_of_simulations'])
        self.s.query(
            "CREATE TABLE fire_origin(name,is_room,x,y,z,floor,sim_id)")
        for sim_id in range(*si.get()):
            seed(sim_id)
            r = self.s.query(
                "SELECT * FROM aamks_geom WHERE type_pri='FIRE'")[0]
            fire_origin = [
                'fire', 1, r['x0'], r['y0'], r['z0'], r['floor'], sim_id
            ]
            self.s.query(
                'INSERT INTO fire_origin VALUES (? , ? , ? , ? , ? , ? , ?)',
                fire_origin)
# }}}

    def _debug(self):  # {{{
        #dd(os.environ['AAMKS_PROJECT'])
        #self.s.dumpall()
        #self.s.dump_geoms()
        #dd(self.s.query("select * from aamks_geom"))
        #dd(self.s.query("select * from world2d"))
        #exit()
        #self.s.dump()
        pass
예제 #8
0
class Navmesh:
    def __init__(self):  # {{{
        ''' 
        installer/navmesh_installer.sh installs all the dependencies.

        * navmesh build from the obj geometry file
        thanks to https://github.com/arl/go-detour !

        * navmesh query
        thanks to https://github.com/layzerar/recastlib/ !

        ============================================

        This is how we are supposed to be called:

        nav=Navmesh()
        nav.build(floor)
        nav.query([(1300,600), (3100,1800)])

            or if you want to block r1 and r2 and have the navmeshes named

        navs=dict()
        navs[('r1','r2')]=Navmesh()
        navs[('r1','r2')].build(floor,('r1','r2'))
        navs[('r1','r2')].query([(1300,600), (3100,1800)])

        '''

        self.json = Json()
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.navmesh = OrderedDict()
# }}}

    def _bricked_wall(self, floor, bypass_rooms=[]):  # {{{
        '''
        For navmesh we may wish to turn some doors into bricks.
        '''

        bricked_wall = []

        if len(bypass_rooms) > 0:
            floors_meta = json.loads(
                self.s.query("SELECT json FROM floors_meta")[0]['json'])
            elevation = floors_meta[floor]['minz_abs']
            where = " WHERE "
            where += " vent_from_name=" + " OR vent_from_name=".join(
                ["'{}'".format(i) for i in bypass_rooms])
            where += " OR vent_to_name=" + " OR vent_to_name=".join(
                ["'{}'".format(i) for i in bypass_rooms])
            bypass_doors = self.s.query(
                "SELECT name,x0,y0,x1,y1 FROM aamks_geom {}".format(where))

            for i in bypass_doors:
                bricked_wall.append([[i['x0'], i['y0'], elevation],
                                     [i['x1'], i['y0'], elevation],
                                     [i['x1'], i['y1'], elevation],
                                     [i['x0'], i['y1'], elevation],
                                     [i['x0'], i['y0'], elevation]])

        bricked_wall += self.json.readdb("obstacles")['obstacles'][floor]
        try:
            bricked_wall.append(self.json.readdb("obstacles")['fire'][floor])
        except:
            pass

        return bricked_wall

# }}}

    def _obj_platform(self, floor):  # {{{
        z = self.s.query(
            "SELECT x0,y0,x1,y1 FROM aamks_geom WHERE type_pri='COMPA' AND floor=?",
            (floor, ))
        platforms = []
        for i in z:
            platforms.append([(i['x1'], i['y1']), (i['x1'], i['y0']),
                              (i['x0'], i['y0']), (i['x0'], i['y1'])])
        return platforms

# }}}

    def _obj_elem(self, face, z):  # {{{
        elem = ''
        elem += "o Face{}\n".format(self._obj_num)
        for verts in face[:4]:
            elem += "v {}\n".format(" ".join(
                [str(i / 100) for i in [verts[0], z, verts[1]]]))
        elem += "f {}\n\n".format(" ".join(
            [str(4 * self._obj_num + i) + "//1" for i in [1, 2, 3, 4]]))
        self._obj_num += 1
        return elem
# }}}

    def _obj_make(self, floor, bypass_rooms):  # {{{
        ''' 
        1. Create obj file from aamks geometries.
        2. Build navmesh with golang, obj is input
        3. Query navmesh with python
        4. bypass_rooms are the rooms excluded from navigation

        99 is the z-dim in cm

        '''

        z = self._bricked_wall(floor, bypass_rooms)
        obj = ''
        self._obj_num = 0
        for face in z:
            obj += self._obj_elem(face, 99)
        for face in self._obj_platform(floor):
            obj += self._obj_elem(face, 0)

        path = "{}/{}.obj".format(os.environ['AAMKS_PROJECT'], self.nav_name)
        with open(path, "w") as f:
            f.write(obj)
        return path

# }}}

    def _navmesh_vis(self, navmesh_paths, colors):  # {{{
        j = Json()
        z = j.read('{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
        for cc, path in enumerate(navmesh_paths):
            for i, p in enumerate(path):
                try:
                    z[self.floor]['lines'].append({
                        "xy": (path[i][0], path[i][1]),
                        "x1":
                        path[i + 1][0],
                        "y1":
                        path[i + 1][1],
                        "strokeColor":
                        colors[cc],
                        "strokeWidth":
                        14,
                        "opacity":
                        0.5
                    })
                except:
                    pass

        j.write(z, '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
# }}}

    def _chunks(self, l, n):  # {{{
        """Yield successive n-sized chunks from l."""
        for i in range(0, len(l), n):
            yield l[i:i + n]
# }}}

    def _get_name(self, floor, bypass_rooms=[]):  # {{{
        brooms = ''
        if len(bypass_rooms) > 0:
            brooms = "-" + "-".join(bypass_rooms)
        self.nav_name = "{}{}.nav".format(floor, brooms)

# }}}

    def test(self):  # {{{
        agents_pairs = 6
        colors = [
            "#f80", "#f00", "#8f0", "#08f", "#f0f", "#f8f", "#0ff", "#ff0"
        ]
        navmesh_paths = []

        z = self.json.read('{}/dd_geoms.json'.format(
            os.environ['AAMKS_PROJECT']))
        evacuees = self.s.query(
            "SELECT x0,y0 FROM aamks_geom WHERE type_pri='EVACUEE' AND floor=? ORDER BY global_type_id LIMIT ?",
            (self.floor, agents_pairs * 2))
        for x, i in enumerate(self._chunks(evacuees, 2)):
            p0 = (i[0]['x0'], i[0]['y0'])
            p1 = (i[1]['x0'], i[1]['y0'])
            z[self.floor]['circles'].append({
                "xy": p0,
                "radius": 30,
                "fillColor": colors[x],
                "opacity": 1
            })
            z[self.floor]['circles'].append({
                "xy": p1,
                "radius": 30,
                "fillColor": colors[x],
                "opacity": 1
            })
            navmesh_paths.append(self.query((p0, p1), 300))
        self.json.write(z,
                        '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
        self._navmesh_vis(navmesh_paths, colors)
        Vis({
            'highlight_geom': None,
            'anim': None,
            'title': 'Nav {} test'.format(self.nav_name),
            'srv': 1
        })
# }}}

    def build(self, floor, bypass_rooms=[]):  # {{{
        self.floor = floor
        self.bypass_rooms = bypass_rooms
        self._get_name(floor, bypass_rooms)
        file_obj = self._obj_make(floor, bypass_rooms)
        file_nav = "{}/{}".format(os.environ['AAMKS_PROJECT'], self.nav_name)
        file_conf = "{}/recast.yml".format(os.environ['AAMKS_PROJECT'])
        with open(file_conf, "w") as f:
            f.write('''\
            cellsize: 0.10
            cellheight: 0.2
            agentheight: 2
            agentradius: 0.30
            agentmaxclimb: 0.1
            agentmaxslope: 45
            regionminsize: 8
            regionmergesize: 20
            edgemaxlen: 12
            edgemaxerror: 1.3
            vertsperpoly: 6
            detailsampledist: 6
            detailsamplemaxerror: 1
            partitiontype: 1
            tilesize: 0
            ''')
        subprocess.call(
            "rm -rf {}; recast --config {} --input {} build {} 1>/dev/null 2>/dev/null"
            .format(file_nav, file_conf, file_obj, file_nav),
            shell=True)

        try:
            self.NAV = dt.dtLoadSampleTileMesh(file_nav)
        except:
            raise SystemExit("Navmesh: cannot create {}".format(file_nav))
# }}}

    def query(self, q, maxStraightPath=16):  # {{{
        '''
        ./Detour/Include/DetourNavMeshQuery.h: maxStraightPath: The maximum number of points the straight path arrays can hold.  [Limit: > 0]
        We set maxStraightPath to a default low value which stops calculations early
        If one needs to get the full path to the destination one must call us with any high value, e.g. 999999999
        '''

        filtr = dt.dtQueryFilter()
        query = dt.dtNavMeshQuery()

        status = query.init(self.NAV, 2048)
        if dt.dtStatusFailed(status):
            return "err", -1, status

        polyPickExt = dt.dtVec3(2.0, 4.0, 2.0)
        startPos = dt.dtVec3(q[0][0] / 100, 1, q[0][1] / 100)
        endPos = dt.dtVec3(q[1][0] / 100, 1, q[1][1] / 100)

        status, out = query.findNearestPoly(startPos, polyPickExt, filtr)
        if dt.dtStatusFailed(status):
            return "err", -2, status
        startRef = out["nearestRef"]
        _startPt = out["nearestPt"]

        status, out = query.findNearestPoly(endPos, polyPickExt, filtr)
        if dt.dtStatusFailed(status):
            return "err", -3, status
        endRef = out["nearestRef"]
        _endPt = out["nearestPt"]

        status, out = query.findPath(startRef, endRef, startPos, endPos, filtr,
                                     maxStraightPath)
        if dt.dtStatusFailed(status):
            return "err", -4, status
        pathRefs = out["path"]

        status, fixEndPos = query.closestPointOnPoly(pathRefs[-1], endPos)
        if dt.dtStatusFailed(status):
            return "err", -5, status

        status, out = query.findStraightPath(startPos, fixEndPos, pathRefs,
                                             maxStraightPath, 0)
        if dt.dtStatusFailed(status):
            return "err", -6, status
        straightPath = out["straightPath"]
        straightPathFlags = out["straightPathFlags"]
        straightPathRefs = out["straightPathRefs"]

        path = []
        for i in straightPath:
            path.append((i[0] * 100, i[2] * 100))

        if path[0] == "err":
            return None
        else:
            return path
# }}}

    def closest_terminal(self, p0, exit_type):  # {{{
        '''
        The shortest polyline defines the closest exit from the floor. 
        dist < 10 test asserts the polyline has min 2 distinct points.

        exit_type: primary | secondary | any
        '''

        if exit_type in ['primary', 'secondary']:
            r = self.s.query(
                "SELECT name,center_x,center_y FROM aamks_geom WHERE terminal_door=? AND floor=?",
                (exit_type, self.floor))
        else:
            r = self.s.query(
                "SELECT name,center_x,center_y FROM aamks_geom WHERE terminal_door IS NOT NULL AND floor=?",
                (self.floor, ))
        m = {}
        closest = {'len': 999999999, 'name': None, 'x': None, 'y': None}
        for i in r:
            if abs(i['center_x'] - p0[0]) < 10 and abs(i['center_y'] -
                                                       p0[1]) < 10:
                closest = {
                    'name': i['name'],
                    'x': i['center_x'],
                    'y': i['center_y'],
                    'len': 0
                }
                return closest
            ll = LineString(
                self.query((p0, (i['center_x'], i['center_y'])), 300)).length
            if ll < closest['len']:
                closest = {
                    'name': i['name'],
                    'x': i['center_x'],
                    'y': i['center_y'],
                    'len': int(ll)
                }
        return closest

# }}}

    def closest_room_escape(self, p0, room):  # {{{
        '''
        Evacuee finds himself in a room with smoke and needs to leave urgently
        '''

        r = self.s.query(
            "SELECT name,center_x,center_y FROM aamks_geom WHERE (vent_from_name=? OR vent_to_name=?) AND floor=?",
            (room, room, self.floor))
        m = {}
        closest = {'len': 999999999, 'name': None, 'x': None, 'y': None}
        for i in r:
            if abs(i['center_x'] - p0[0]) < 10 and abs(i['center_y'] -
                                                       p0[1]) < 10:
                closest = {
                    'name': i['name'],
                    'x': i['center_x'],
                    'y': i['center_y'],
                    'len': 0
                }
                return closest
            ll = sqrt((i['center_x'] - p0[0])**2 + (i['center_y'] - p0[1])**2)
            if ll < closest['len']:
                closest = {
                    'name': i['name'],
                    'x': i['center_x'],
                    'y': i['center_y'],
                    'len': int(ll)
                }
        return closest
예제 #9
0
class DojazdMW:
    def __init__(self):  # {{{
        if len(sys.argv) < 2:
            self.zbior = 'office123/sesja1'
        else:
            self.zbior = sys.argv[1]
        self.json = Json()
        self.debug = 1
        self.make_segments_map()
        self.make_db_czynnosci()
        self.s = Sqlite("sqlite/firetrucks.db")
        self.main()
# }}}

    def query(self, param, dlugosc):  # {{{
        if isinstance(self.db_czynnosci[param], dict):
            for t in list(self.db_czynnosci[param].keys()):
                if t >= dlugosc:
                    return self.db_czynnosci[param][t]
        else:
            return self.db_czynnosci[param]

# }}}

    def make_db_czynnosci(self):  # {{{
        self.db_czynnosci = {
            't_zasilenie_samochodu':
            OrderedDict([(20, 30)]),
            't_zasilenie_instalacji_w75x1':
            OrderedDict([(20, 70)]),
            't_zasilenie_instalacji_w75x2':
            OrderedDict([(20, 100)]),
            't_sprawianie_motopompy_MP81_z_linia_w75':
            OrderedDict([(20, 320)]),
            't_sprawianie_motopompy_szlam_z_linia_w75':
            OrderedDict([(20, 510)]),
            't_sprawianie_zbiornika_2i5m3':
            290,
            't_sprawianie_zbiornika_5m3':
            150,
            't_linia_główna_w75_do_rozdzielacza':
            30,
            't_linia_główna_w75x2_do_rozdzielacza_harmonijka':
            15,
            't_przygotowanie_roty_gaśn':
            50,
            'v_rota_gaśn_zewn_poziom_dym0':
            1,
            'v_rota_gaśn_wewn_poziom_dym0':
            0.8,
            'v_rota_gaśn_wewn_poziom_dym1':
            0.38,
            't_rota_gaśn_wewn_pion_dym0':
            OrderedDict([(12, 100), (25, 330), (55, 1026)]),
            't_rota_gaśn_wewn_pion_dym1':
            OrderedDict([(12, 140), (25, 500), (55, 1520)]),
            'v_linia_gaśn_w52_wewn_poziom_dym1_kregi':
            0.25,
            't_linia_gaśn_w52_wewn_pion_dym1_kregi_1rota':
            OrderedDict([(12, 620), (25, 1120), (55, 2120)]),
            't_linia_gaśn_w52_wewn_pion_dym1_kregi_2roty':
            OrderedDict([(12, 500), (25, 1060), (55, 1520)]),
            'v_linia_gaśn_w52_wewn_poziom_dym0_kregi':
            0.8,
            't_linia_gaśn_w52_wewn_pion_dym0_kregi_1rota':
            OrderedDict([(12, 230), (25, 760), (55, 1700)]),
            't_linia_gaśn_w52_wewn_pion_dym0_kregi_2roty':
            OrderedDict([(12, 170), (25, 700), (55, 1520)]),
            'v_linia_gaśn_w42_wewn_poziom_dym1_kregi':
            0.26,
            't_linia_gaśn_w42_wewn_pion_dym1_kregi_1rota':
            OrderedDict([(12, 620), (25, 1120), (55, 2120)]),
            't_linia_gaśn_w42_wewn_pion_dym1_kregi_2roty':
            OrderedDict([(12, 500), (25, 1060), (55, 1520)]),
            'v_linia_gaśn_w42_wewn_poziom_dym0_kregi':
            1,
            't_linia_gaśn_w42_wewn_pion_dym0_kregi_1rota':
            OrderedDict([(12, 230), (25, 760), (55, 1700)]),
            't_linia_gaśn_w42_wewn_pion_dym0_kregi_2roty':
            OrderedDict([(12, 170), (25, 700), (55, 1520)]),
            'v_linia_gaśn_w52_wewn_poziom_dym1_kasetony':
            0.4,
            't_linia_gaśn_w52_wewn_pion_dym1_kasetony_1rota':
            OrderedDict([(12, 560), (25, 1000), (55, 1940)]),
            't_linia_gaśn_w52_wewn_pion_dym1_kasetony_2roty':
            OrderedDict([(12, 320), (25, 880), (55, 1510)]),
            'v_linia_gaśn_w52_wewn_poziom_dym0_kasetony':
            1,
            't_linia_gaśn_w52_wewn_pion_dym0_kasetony_1rota':
            OrderedDict([(12, 500), (25, 940), (55, 1880)]),
            't_linia_gaśn_w52_wewn_pion_dym0_kasetony_2roty':
            OrderedDict([(12, 320), (25, 700), (55, 1510)]),
            'v_linia_gaśn_w42_wewn_poziom_dym1_kasetony':
            0.4,
            't_linia_gaśn_w42_wewn_pion_dym1_kasetony_1rota':
            OrderedDict([(12, 500), (25, 940), (55, 1940)]),
            't_linia_gaśn_w42_wewn_pion_dym1_kasetony_2roty':
            OrderedDict([(12, 320), (25, 880), (55, 1510)]),
            'v_linia_gaśn_w42_wewn_poziom_dym0_kasetony':
            1.33,
            't_linia_gaśn_w42_wewn_pion_dym0_kasetony_1rota':
            OrderedDict([(12, 500), (25, 940), (55, 1880)]),
            't_linia_gaśn_w42_wewn_pion_dym0_kasetony_2roty':
            OrderedDict([(12, 320), (25, 700), (55, 1510)]),
            't_linia_gaśn_w52_wewn_pion_dym1_dusza_klatki_2roty':
            OrderedDict([(12, 500), (25, 940), (55, 1940)]),
            't_linia_gaśn_w52_wewn_pion_dym1_dusza_klatki_3roty':
            OrderedDict([(12, 320), (25, 880), (55, 1510)]),
            't_linia_gaśn_w52_wewn_pion_dym0_dusza_klatki_2roty':
            OrderedDict([(12, 500), (25, 940), (55, 1880)]),
            't_linia_gaśn_w52_wewn_pion_dym0_dusza_klatki_3roty':
            OrderedDict([(12, 320), (25, 700), (55, 1510)]),
            't_linia_gaśn_w42_wewn_pion_dym1_dusza_klatki_2roty':
            OrderedDict([(12, 500), (25, 940), (55, 1940)]),
            't_linia_gaśn_w42_wewn_pion_dym1_dusza_klatki_3roty':
            OrderedDict([(12, 320), (25, 880), (55, 1510)]),
            't_linia_gaśn_w42_wewn_pion_dym0_dusza_klatki_2roty':
            OrderedDict([(12, 440), (25, 940), (55, 1880)]),
            't_linia_gaśn_w42_wewn_pion_dym0_dusza_klatki_3roty':
            OrderedDict([(12, 320), (25, 700), (55, 1510)]),
            't_szybkie_natarcie_zewn_poziom':
            OrderedDict([(20, 50)]),
            't_szybkie_natarcie_zewn_pion_elewacja':
            OrderedDict([(12, 190)]),
            't_linia_gaśn_w52_elewacja':
            OrderedDict([(12, 440), (25, 880), (55, 2120)]),
            't_linia_gaśn_w42_elewacja':
            OrderedDict([(12, 430), (25, 860), (55, 1880)]),
            't_przygotowanie_działań_drabina_dw10':
            OrderedDict([(20, 260)]),
            't_wejście_oknem_drabina_dw10':
            OrderedDict([(20, 230)]),
            't_przygotowanie_działań_drabina_nasadkowa':
            OrderedDict([(20, 280)]),
            't_wejście_oknem_drabina_nasadkowa':
            OrderedDict([(20, 250)]),
            't_przygotowanie_działań_drabina_mechaniczna':
            OrderedDict([(12, 160), (25, 180), (55, 400)]),
            't_przygotowanie_działań_podnośnik':
            OrderedDict([(12, 250), (25, 290), (55, 490)]),
            't_przygotowanie_sprzęt_wentylacja':
            OrderedDict([(20, 120)]),
            't_przygotowanie_roty_gotowość':
            25,
            't_przygotowanie_medyczne':
            70,
            't_przygotowanie_monitorowania_aparatów_powietrznych':
            30,
            't_zabezpieczenie_pachołkami':
            170,
            't_rozpoznanie_wstepne_3600':
            70,
            't_przygotowanie_asekuracji_drabina_dw10':
            OrderedDict([(20, 210)]),
            't_przygotowanie_asekuracji_drabina_nasadkowa':
            OrderedDict([(20, 230)]),
            't_przygotowanie_asekuracji_drabina_mechaniczna':
            OrderedDict([(12, 140), (25, 160), (55, 380)]),
            't_przygotowanie_asekuracji_podnośnik':
            OrderedDict([(12, 230), (25, 260), (55, 460)]),
            't_przygotowanie_skokochronu':
            OrderedDict([(20, 130)]),
            't_przygotowanie_asekuracji_rota_RIT':
            110,
            't_dotarcie_roty_do_dźwigu_rozpoznanie_bojem':
            OrderedDict([(20, 10)]),
            'v_nie_gaśnicza_wewn_poziom_dym0':
            1.33,
            'v_zewn':
            2,
            'v_nie_gaśnicza_wewn_pion_dym0':
            OrderedDict([(12, 100), (25, 220), (55, 1060)]),
            't_wyważanie_drzwi_drewniane_dym0':
            80,
            't_wyważanie_drzwi_drewniane_dym1':
            170,
            't_wyważanie_drzwi_antywłamaniowe_dym0':
            450,
            't_wyważanie_drzwi_antywłamaniowe_dym1':
            740,
        }

# }}}

    def make_segments_map(self):  # {{{

        self.segments_map = {
            '0000000000000001': 'wewn_dym0_poziom',
            '0000000000000101': 'wewn_dym0_pion',
            '0000000000001001': 'wewn_dym0_dzwig',
            '0000000000100001': 'wewn_dym0_poziom_lina_elewacja',
            '0000000000000011': 'wewn_dym1_poziom',
            '0000000000000111': 'wewn_dym1_pion',
            '0000000000001011': 'wewn_dym1_dzwig',
            '0000000000100011': 'wewn_dym1_poziom_lina_elewacja',
            '0000001100000000': 'zewn_poziom',
            '0000010100000000': 'zewn_drabina_przystawna',
            '0000100100000000': 'zewn_drabina_mechaniczna',
            '0000000100000000': 'zewn_pion',
            '0000000000100101': 'wewn_pion_lina_elewacja',
            '0000000000010101': 'wewn_dym0_hydrant_pion',
            '0000000000010001': 'wewn_dym0_hydrant_poziom',
            '0000000000010011': 'wewn_dym1_hydrant_poziom',
        }
# }}}

    def save(self, results):  # {{{
        x = json.dumps({'results': results, 'conf': self.conf})
        if self.conf['status'] == 'Start':
            with open('symulacje/{}/wyniki.txt'.format(self.zbior), "w") as f:
                f.write(x + "\n")
        else:
            with open('symulacje/{}/wyniki.txt'.format(self.zbior), "a") as f:
                f.write(x + "\n")

        if self.conf['status'] == 'Koniec':
            os.system("python3 results.py '{}'".format(self.zbior))

# }}}

    def czy_wykluczamy_bo_droga(self, wariant, data):  # {{{
        # TODO: jak sumujemy suma_segmentow, bo duże długości mamy
        total_w52 = 0
        total_w75 = 0
        for s in self.conf['samochody']:
            total_w52 += int(
                self.s.query("select w_52,w_75 from Generics where id=?",
                             (s['id'], ))[0]['w_52'])
            total_w75 += int(
                self.s.query("select w_52,w_75 from Generics where id=?",
                             (s['id'], ))[0]['w_75'])
        total = total_w75 + total_w52

        suma_segmentow = 0
        for i in data['segmenty']:
            suma_segmentow += i['długość']

        if suma_segmentow > total:
            if self.debug == 1:
                print("{}: wykluczam bo droga. Droga:{}[m], Węży:{}[m]".format(
                    wariant, round(suma_segmentow), total))
            return 1
        else:
            return 0
# }}}

    def czy_wykluczamy(self, wariant, data):  # {{{
        if self.czy_wykluczamy_bo_droga(wariant, data) == 1:
            return 1
# }}}

    def wewn_dym0_poziom(self, segment):  # {{{
        # TODO: kiedy która prędkość?

        #  bit1=1  rozwinięcie podstawowe
        if segment['wariant'][-2] == '1':
            if self.weze_nawodnione == 1:
                return segment['długość'] / self.query(
                    "v_linia_gaśn_w52_wewn_poziom_dym0_kregi",
                    segment['długość'])
            else:
                return segment['długość'] / self.query(
                    "v_rota_gaśn_wewn_poziom_dym0", segment['długość'])

        #  bit1=0  rozwinięcie niepodstawowe, czyli gaśnica?
        else:
            return segment['długość'] / self.query(
                "v_rota_gaśn_wewn_poziom_dym0", segment['długość'])
# }}}

    def wewn_dym0_pion(self, segment):  # {{{
        # TODO: kiedy która prędkość?
        # 'v_nie_gaśnicza_wewn_pion_dym0'                         : OrderedDict([(12,100), (25,220), (55,1060)]),

        #  bit1=1  rozwinięcie podstawowe
        if segment['wariant'][-2] == '1':
            if self.weze_nawodnione == 1:
                return self.query(
                    "t_linia_gaśn_w52_wewn_pion_dym0_kregi_1rota",
                    segment['długość'])
            else:
                return self.query("t_rota_gaśn_wewn_pion_dym0",
                                  segment['długość'])

        #  bit1=0  rozwinięcie niepodstawowe, czyli gaśnica?
        else:
            return self.query("t_rota_gaśn_wewn_pion_dym0", segment['długość'])
# }}}

    def wewn_dym0_dzwig(self, segment):  # {{{
        pieter_w_podrozy = segment['długość'] / 3
        pieter_w_budynku = self.conf['ogólne']['liczba_pięter']
        return 60 * pieter_w_podrozy / pieter_w_budynku
# }}}

    def wewn_dym0_hydrant_poziom(self, segment):  # {{{
        # 't_sprawianie_hydrantu_podziemnego'                     : 70,
        # 't_sprawianie_hydrantu_naziemnego'                      : 30,
        self.weze_nawodnione = 1
        return 30

# }}}

    def wewn_dym0_hydrant_pion(self, segment):  # {{{
        # 't_sprawianie_hydrantu_podziemnego'                     : 70,
        # 't_sprawianie_hydrantu_naziemnego'                      : 30,
        return 0

# }}}

    def wewn_dym1_poziom(self, segment):  # {{{
        #  bit1=1  rozwinięcie podstawowe
        if segment['wariant'][-2] == '1':
            if self.weze_nawodnione == 1:
                return segment['długość'] / self.query(
                    "v_linia_gaśn_w52_wewn_poziom_dym1_kregi",
                    segment['długość'])
            else:
                return segment['długość'] / self.query(
                    "v_rota_gaśn_wewn_poziom_dym1", segment['długość'])

        #  bit1=0  rozwinięcie niepodstawowe, czyli gaśnica?
        else:
            return segment['długość'] / self.query(
                "v_rota_gaśn_wewn_poziom_dym1", segment['długość'])
# }}}

    def wewn_dym1_pion(self, segment):  # {{{
        # TODO: kiedy która prędkość?

        #  bit1=1  rozwinięcie podstawowe
        if segment['wariant'][-2] == '1':
            if self.weze_nawodnione == 1:
                return self.query(
                    "t_linia_gaśn_w52_wewn_pion_dym1_kregi_1rota",
                    segment['długość'])
            else:
                return self.query("t_rota_gaśn_wewn_pion_dym1",
                                  segment['długość'])

        #  bit1=0  rozwinięcie niepodstawowe, czyli gaśnica?
        else:
            return self.query("t_rota_gaśn_wewn_pion_dym1", segment['długość'])
# }}}

    def wewn_dym1_dzwig(self, segment):  # {{{
        pieter_w_podrozy = segment['długość'] / 3
        pieter_w_budynku = self.conf['ogólne']['liczba_pięter']
        return 60 * pieter_w_podrozy / pieter_w_budynku
# }}}

    def wewn_dym1_hydrant_poziom(self, segment):  # {{{
        # TODO: skąd dane?
        dd(segment)
# }}}

    def wewn_dym0_poziom_lina_elewacja(self, segment):  # {{{
        # todo
        return 0
# }}}

    def wewn_dym1_poziom_lina_elewacja(self, segment):  # {{{
        # todo
        return 0
# }}}

    def wewn_pion_lina_elewacja(self, segment):  # {{{
        return 0

    # }}}
    def zewn_poziom(self, segment):  # {{{
        return segment['długość'] / self.query("v_zewn", segment['długość'])
# }}}

    def zewn_pion(self, segment):  # {{{
        return 0
# }}}

    def zewn_drabina_przystawna(self, segment):  # {{{
        zdjecie_drabiny = 60
        bieg_z_drabina = segment['długość'] * 1.36
        drabine_spraw = 190
        wspinaczka = 20

        return zdjecie_drabiny + bieg_z_drabina + drabine_spraw + wspinaczka
# }}}

    def zewn_drabina_mechaniczna(self, segment):  # {{{
        # TODO, sprawdzić
        # 't_przygotowanie_działań_drabina_mechaniczna'           : OrderedDict([(12,160), (25,180), (55,400)]),
        # 't_przygotowanie_asekuracji_drabina_mechaniczna'        : OrderedDict([(12,140), (25,160), (55,380)]),

        if segment['wariant'][-9] == '1':
            return self.query("t_przygotowanie_asekuracji_drabina_mechaniczna",
                              segment['długość'])
        else:
            return self.query("t_przygotowanie_działań_drabina_mechaniczna",
                              segment['długość'])
# }}}

    def main(self):  # {{{
        self.weze_nawodnione = 0
        xj = self.json.read('symulacje/{}/scenariusz.json'.format(self.zbior))
        self.warianty = xj['warianty']
        self.conf = xj['conf']
        results = OrderedDict()
        for wariant, data in self.warianty.items():
            if self.czy_wykluczamy(wariant, data) == 1:
                continue
            czas_wariantu = 0
            debug = []
            for s in data['segmenty']:
                if s['segment'] not in self.segments_map:
                    #print("ignoruję nierozpoznany segment: {}".format(s['segment']))
                    debug.append('{},s:{},t:{} '.format(
                        s['segment'], None, None))
                    continue
                else:
                    #s['segment']='0000010100000000' # temp zewn_drabina_przystawna()
                    #s['segment']='0000000000000001' # temp wewn_dym0_poziom()
                    #s['segment']='0000000000001001' # temp wewn_dym0_dzwig()
                    #s['segment']='0000000000010001' # temp wewn_dym0_hydrant()
                    #s['segment']='0000100100000000' #'zewn_drabina_mechaniczna()

                    handler = getattr(self, self.segments_map[s['segment']])
                    s['segmentx'] = self.segments_map[s['segment']]
                    s['wariant'] = wariant
                    czas = handler(s)
                    if czas == None:  # trzeba wyłączyć i w każdej funkcji zadbać o return not None
                        czas = 999
                    czas_wariantu += czas
                    debug.append('{},s:{},t:{} '.format(
                        s['segmentx'], s['długość'], round(czas)))
            results[wariant] = {'wynik': round(czas_wariantu), 'debug': debug}
        if self.debug == 1:
            dd("Udane:", results)
        self.save(results)
예제 #10
0
 def calculate_building_area(self):
     s = Sqlite("{}/aamks.sqlite".format(self.dir))
     result = s.query("SELECT sum(room_area) as total FROM aamks_geom")
     return result[0]['total']
예제 #11
0
파일: worker.py 프로젝트: kowalskiw/aamks
class Worker:
    def __init__(self):
        self.start_time = time.time()
        self.url = sys.argv[1]
        self.vars = OrderedDict()
        self.results = dict()
        self.obstacles = None
        self.trajectory = None
        self.velocity = None
        self.floor_dims = None
        self.evacuees = None
        self.fire_dto = None
        self.sim_floors = None
        self.start_time = time.time()
        self.floors = list()
        self.host_name = os.uname()[1]
        os.chdir('/home/aamks_users')
        self.working_dir = self.url.split('aamks/')[1]
        self.cross_building_results = None

    def _report_error(self, exception: Exception) -> logging:
        print('Error occurred, see aamks.log file for details.')
        logging.error('Cannot create RVO2 environment: {}'.format(
            str(exception)))
        sys.exit(1)

    def get_config(self):
        try:
            f = open(
                '{}/{}/config.json'.format(os.environ['AAMKS_PATH'], 'evac'),
                'r')
            self.config = json.load(f)
        except Exception as e:
            print('Cannot read config file: {}'.format(e))
            sys.exit(1)

        try:
            self.vars['conf'] = json.loads(
                urlopen('{}/evac.json'.format(self.url)).read().decode())
        except Exception as e:
            print('Cannot fetch evac.json from server: {}'.format(str(e)))
            sys.exit(1)
        else:
            print('URL OK. Starting calculations')

        self.sim_id = self.vars['conf']['SIM_ID']
        self.host_name = os.uname()[1]
        self.db_server = self.vars['conf']['SERVER']

    def get_geom_and_cfast(self):

        os.chdir(self.working_dir)

        logging.basicConfig(filename='aamks.log',
                            level=logging.INFO,
                            format='%(asctime)s %(levelname)s: %(message)s')

        logging.info('URL: {}'.format(self.url))

        try:
            urlretrieve('{}/../../aamks.sqlite'.format(self.url),
                        'aamks.sqlite')
            urlretrieve('{}/../../0.obj'.format(self.url), '0.obj')

        except Exception as e:
            self._report_error(e)
        else:
            logging.info('Aamks.sqlite fetched from server')

        try:
            self.cfast_input = urlopen('{}/cfast.in'.format(
                self.url)).read().decode()

        except Exception as e:
            self._report_error(e)
        else:
            logging.info('Cfast.in fetched from server')
        print("Host: {} start simulation id: {}".format(
            self.host_name, self.sim_id))

    def _create_workspace(self):
        try:
            shutil.rmtree(self.working_dir, ignore_errors=True)
            os.makedirs(self.working_dir)
        except Exception as e:
            self._report_error(e)

    def run_cfast_simulations(self):

        try:
            with open('cfast.in', "w") as f:
                f.write(self.cfast_input)
        except Exception as e:
            self._report_error(e)
        else:
            logging.debug('Cfast input file saved.')

        try:
            os.system('/usr/local/aamks/fire/cfast cfast.in')
            cfast_log = open('cfast.log', 'r')
        except Exception as e:
            self._report_error(e)

        for line in cfast_log.readlines():
            if line.startswith("***Error:"):
                self._report_error(Exception(line))

        logging.info('Simulation finished with exit code 0')

    def create_geom_database(self):

        self.s = Sqlite("aamks.sqlite")
        #self.s.dumpall()
        self.obstacles = json.loads(
            self.s.query('SELECT * FROM obstacles')[0]['json'],
            object_pairs_hook=OrderedDict)

    def _create_evacuees(self, floor):

        evacuees = []
        logging.debug('Adding evacuues on floor: {}'.format(floor))

        floor = self.vars['conf']['FLOORS_DATA'][str(floor)]

        for i in floor['EVACUEES'].keys():
            evacuees.append(
                Evacuee(origin=tuple(floor['EVACUEES'][i]['ORIGIN']),
                        v_speed=floor['EVACUEES'][i]['V_SPEED'],
                        h_speed=floor['EVACUEES'][i]['H_SPEED'],
                        pre_evacuation=floor['EVACUEES'][i]['PRE_EVACUATION'],
                        alpha_v=floor['EVACUEES'][i]['ALPHA_V'],
                        beta_v=floor['EVACUEES'][i]['BETA_V'],
                        node_radius=self.config['NODE_RADIUS']))

        e = Evacuees()
        [e.add_pedestrian(i) for i in evacuees]

        logging.info('Num of evacuees placed: {}'.format(len(evacuees)))
        return e

    def prepare_simulations(self):
        logging.info('Number of floors processed: {}'.format(
            len(self.obstacles['points'])))
        obstacles = []

        for i in range(len(self.obstacles['points'])):
            try:
                eenv = EvacEnv(self.vars['conf'])
                eenv.floor = i
            except Exception as e:
                self._report_error(e)
            else:
                logging.info('RVO2 ready on {} floors'.format(i))

            for obst in self.obstacles['points'][str(i)]:
                obstacles.append([tuple(x) for x in obst])
            eenv.obstacle = obstacles
            num_of_vertices = eenv.process_obstacle(obstacles)
            eenv.generate_nav_mesh()
            logging.info(
                'Added obstacles on floor: {}, number of vercites: {}'.format(
                    str(i + 1), num_of_vertices))

            e = self._create_evacuees(i)
            eenv.place_evacuees(e)
            self.floors.append(eenv)

    def do_simulation(self):
        logging.info('Starting simulations')
        master_query = None

        time_frame = 10
        floor = 0
        try:
            master_query = SmokeQuery(floor='0')
        except Exception as e:
            self._report_error(e)

        for i in self.floors:
            try:
                i.smoke_query = master_query
            except Exception as e:
                self._report_error(e)
            else:
                logging.info(
                    'Smoke query connected to floor: {}'.format(floor))
            floor += 1
        while 1:
            x = master_query.cfast_has_time(time_frame)
            if master_query.cfast_has_time(time_frame) == 1:
                logging.info('Simulation time: {}'.format(time_frame))
                l = []
                for i in self.floors:
                    i.read_cfast_record(time_frame)
                    i.do_simulation(time_frame)
                    l.append(i.rset)
                time_frame += 10
            else:
                time.sleep(1)
            if time_frame > (self.vars['conf']['general']['simulation_time'] -
                             10):
                break
            if prod(array(l)) > 0:
                break

        self.cross_building_results = master_query.get_final_vars()

    def send_report(self):  # {{{
        '''
        Runs on a worker. Write /home/aamks/project/sim_id.json on each aRun
        completion. Then inform gearman server to scp to itself
        /home/aamks/project/sim_id.json via aOut service. Gearman server will
        process this json via /usr/local/aamks/manager/results_collector.py.
        Gearman server will psql insert and will scp the worker's animation to
        itself.
        '''
        self._write_animation_zips()
        self._write_meta()

        Popen("gearman -h {} -f aOut '{} {} {}'".format(
            os.environ['AAMKS_SERVER'], self.host_name,
            '/home/aamks_users/' + self.working_dir + '/' + self.meta_file,
            self.sim_id),
              shell=True)
        #print("gearman -h {} -f aOut '{} {} {}'".format(os.environ['AAMKS_SERVER'], self.host_name, '/home/aamks_users/'+self.working_dir+'/'+self.meta_file, self.sim_id) )

    # }}}
    def _write_animation_zips(self):  # {{{
        '''
        Raw data comes as an argument. We create /home/aamks/1.anim.zip
        with anim.json inside.
        '''

        floor = 0
        for i in self.floors:
            animation_data = i.record_data()
            zf = zipfile.ZipFile("f{}_s{}.anim.zip".format(floor, self.sim_id),
                                 mode='w',
                                 compression=zipfile.ZIP_DEFLATED)
            try:
                zf.writestr("anim.json", json.dumps(animation_data))
            finally:
                zf.close()
            floor += 1

    # }}}
    def _write_meta(self):  # {{{
        j = Json()
        report = OrderedDict()
        report['worker'] = self.host_name
        report['sim_id'] = self.sim_id
        report['project_id'] = self.vars['conf']['project_id']
        report[
            'path_to_project'] = '/home/aamks_users/' + self.working_dir.split(
                'workers')[0]
        report['fire_origin'] = self.vars['conf']['ROOM_OF_FIRE_ORIGIN']
        report['highlight_geom'] = None
        report['psql'] = dict()
        report['psql']['fed'] = dict()
        report['psql']['rset'] = dict()
        report['psql']['runtime'] = int(time.time() - self.start_time)
        report['psql']['cross_building_results'] = self.cross_building_results
        for i in self.floors:
            report['psql']['fed'][i.floor] = i.fed
            report['psql']['rset'][i.floor] = int(i.rset)
        for num_floor in range(len(self.floors)):
            report['animation'] = "f{}_s{}.anim.zip".format(
                num_floor, self.sim_id)
            report['floor'] = num_floor

        self.meta_file = "meta_{}.json".format(self.sim_id)
        j.write(report, self.meta_file)

    # }}}

    def main(self):
        self.get_config()
        SendMessage('Worker: {} start_sim: {}'.format(self.host_name,
                                                      self.sim_id))
        self._create_workspace()
        self.get_geom_and_cfast()
        self.create_geom_database()
        self.run_cfast_simulations()
        self.prepare_simulations()
        self.do_simulation()
        self.send_report()
        SendMessage('Worker: {} end sim: {}'.format(self.host_name,
                                                    self.sim_id))

    def test(self):
        self.get_config()
        self.get_geom_and_cfast()
        self.create_geom_database()
        self.prepare_simulations()
        self.do_simulation()
        self.send_report()
예제 #12
0
class EvacEnv:
    def __init__(self):  # {{{
        self.Que = Staircase(floors=9)
        self.json = Json()
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))

        self.evacuee_radius = self.json.read("{}/inc.json".format(
            os.environ['AAMKS_PATH']))['evacueeRadius']
        time = 1
        #self.sim rvo2.PyRVOSimulator TIME_STEP , NEIGHBOR_DISTANCE , MAX_NEIGHBOR , TIME_HORIZON , TIME_HORIZON_OBSTACLE , RADIUS , MAX_SPEED
        self.sim = rvo2.PyRVOSimulator(time, 40, 5, time, time,
                                       self.evacuee_radius, 30)
        self._anim = {
            "simulation_id": 1,
            "simulation_time": 20,
            "time_shift": 0,
            "animations": {
                "evacuees": [],
                "rooms_opacity": []
            }
        }
        self._create_agents()
        self._load_obstacles()
        Vis({
            'highlight_geom': None,
            'anim': '1/f1.zip',
            'title': 'x',
            'srv': 1
        })
        self.waitings = {}

# }}}

    def _create_agents(self):  # {{{
        #self.s.query("INSERT INTO aamks_geom (name, type_pri, x0, y0) VALUES ('f500', 'EVACUEE', '690', '2300')")
        z = self.s.query("SELECT * FROM aamks_geom WHERE type_pri='EVACUEE'")
        self.agents = {}
        for i in z:
            aa = i['name']
            self.agents[aa] = {}
            ii = self.sim.addAgent((i['x0'], i['y0']))
            self.agents[aa]['name'] = aa
            self.agents[aa]['id'] = ii
            self.sim.setAgentPrefVelocity(ii, (0, 0))
            self.agents[aa]['behaviour'] = 'random'
            self.agents[aa]['origin'] = (i['x0'], i['y0'])
            if int(aa[1:]) < 105:
                self.agents[aa]['target'] = (1010, i['y0'])
            else:
                self.agents[aa]['target'] = (2530, i['y0'])
        self._positions()
# }}}

    def _load_obstacles(self):  # {{{
        z = self.json.readdb('obstacles')
        obstacles = z['obstacles']['0']
        for i in obstacles:
            self.sim.addObstacle([(o[0], o[1]) for o in i[:4]])
            self.sim.processObstacles()
# }}}

    def _velocity(self, a):  # {{{
        '''
        radius=3.5 is the condition for the agent to reach the behind-doors target 
        '''

        dx = a['target'][0] - self.sim.getAgentPosition(a['id'])[0]
        dy = a['target'][1] - self.sim.getAgentPosition(a['id'])[1]
        self.sim.setAgentPrefVelocity(a['id'], (dx, dy))
        if abs(dx) < 60:
            if self.sim.getAgentPosition(a['id'])[1] < 300:
                floor = 8
            elif self.sim.getAgentPosition(
                    a['id'])[1] > 340 and self.sim.getAgentPosition(
                        a['id'])[1] < 640:
                floor = 7
            elif self.sim.getAgentPosition(
                    a['id'])[1] > 680 and self.sim.getAgentPosition(
                        a['id'])[1] < 980:
                floor = 6
            elif self.sim.getAgentPosition(
                    a['id'])[1] > 1020 and self.sim.getAgentPosition(
                        a['id'])[1] < 1320:
                floor = 5
            elif self.sim.getAgentPosition(
                    a['id'])[1] > 1360 and self.sim.getAgentPosition(
                        a['id'])[1] < 1660:
                floor = 4
            elif self.sim.getAgentPosition(
                    a['id'])[1] > 1700 and self.sim.getAgentPosition(
                        a['id'])[1] < 2000:
                floor = 3
            elif self.sim.getAgentPosition(
                    a['id'])[1] > 2040 and self.sim.getAgentPosition(
                        a['id'])[1] < 2340:
                floor = 2
            elif self.sim.getAgentPosition(
                    a['id'])[1] > 2380 and self.sim.getAgentPosition(
                        a['id'])[1] < 2680:
                floor = 1
            else:
                floor = 0
            try:
                if (a['name'], a['id']) not in self.waitings[floor]:
                    self.waitings[floor].append((a['name'], a['id']))
            except:
                self.waitings.setdefault(floor, []).append(
                    (a['name'], a['id']))
        return sqrt(dx**2 + dy**2)
# }}}

    def _positions(self):  # {{{
        frame = []
        for k, v in self.agents.items():
            if self.Que.check_if_in(v['id']):
                pos = self.Que.check_if_in(v['id'])
            else:
                pos = [round(i) for i in self.sim.getAgentPosition(v['id'])]
            frame.append([pos[0], pos[1], 0, 0, "N", 1])
        self._anim["animations"]["evacuees"].append({"0": frame})
# }}}

    def _update(self):  # {{{
        for k, v in self.agents.items():
            target_dist = self._velocity(self.agents[k])
            if target_dist <= self.evacuee_radius * 3.5:
                #dd(self.agents[k]['id'], target_dist)
                pass
                #exit()
        self._positions()


# }}}

    def _write_zip(self):  # {{{
        d = "{}/workers/1".format(os.environ['AAMKS_PROJECT'])
        #dd(self._anim['animations']['evacuees'])

        zf = zipfile.ZipFile("{}/f1.zip".format(d), 'w')
        zf.writestr("anim.json", json.dumps(self._anim))
        zf.close()  # }}}
        #self.json.write(self._anim, "/home/mateusz/Pulpit/praca/anim3.json")
    def _add_to_staircase(self):  # {{{
        try:
            for floor in sorted(self.waitings.keys()):
                #print(floor, len(self.waitings[floor]))
                #liczba oczekujących na danym piętrze
                agentname, agentid = self.waitings[floor][0]
                if self.Que.add_to_queues(floor, agentid):
                    #self.agents[agentname]['target']=(19750, 3570)
                    #self.sim.setAgentPosition(agentid, (1750,3570))
                    self.agents[agentname]['target'] = (19750, 3570)
                    self.sim.setAgentPosition(agentid, (3750, 3570))
                    del self.waitings[floor][0]
                    if len(self.waitings[floor]) == 0:
                        del self.waitings[floor]
        except:
            pass  # }}}

    def _run(self):  # {{{
        x = []  #krok
        y = []  #prędkość w klatce
        xx = []  #gęstość
        yyy = []
        ay = []  #prędkość w klatce i poza
        done = []
        done2 = []
        donex = []
        for t in range(550):
            self.sim.doStep()
            self._update()
            self._add_to_staircase()
            donex.append(t)

            K = [copy.deepcopy(k.counter) for k in self.Que.ques]
            wszyscy = self.Que.total_number_of_people()
            done.append(wszyscy)
            done2.append(self.Que.total_completed())
            wszyscy2 = 0
            for i in self.waitings.values():
                wszyscy2 += len(i)

            self.Que.move()

            J = [j.counter for j in self.Que.ques]
            krok = 0
            stop = 0
            for i in range(len(K)):
                krok += len([
                    x[0] for x, y in zip(K[i].items(), J[i].items())
                    if x[1][2] != y[1][2] and len(y[1]) < 4
                ])
                krok += len([
                    x for x, y in zip(K[i].items(), J[i].items())
                    if len(x[1]) != len(y[1])
                ])
                stop += len([
                    x[0] for x, y in zip(K[i].items(), J[i].items())
                    if x[1][2] == y[1][2] and len(y[1]) < 4
                ])
            if wszyscy > 0:
                a = self.Que.density2(wszyscy)
                xx.append(a)
                y.append(round(krok / wszyscy * 100, 2))
                x.append(t)
                ay.append(round(krok / (wszyscy + wszyscy2) * 100, 2))
                #yy = 0.42*(1/a)**(1/3)
                #yyy.append(yy)
            if wszyscy == 0 and t > 100:
                break
예제 #13
0
class EvacEnv:
    def __init__(self):  # {{{
        self.json = Json()
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))

        self.evacuee_radius = self.json.read("{}/inc.json".format(
            os.environ['AAMKS_PATH']))['evacueeRadius']
        time = 1
        #self.sim rvo2.PyRVOSimulator TIME_STEP , NEIGHBOR_DISTANCE , MAX_NEIGHBOR , TIME_HORIZON , TIME_HORIZON_OBSTACLE , RADIUS , MAX_SPEED
        self.sim = rvo2.PyRVOSimulator(time, 40, 5, time, time,
                                       self.evacuee_radius, 30)
        self.door1 = 'd10'
        self.door2 = (6100, 2100)
        self._create_teleports()
        self._create_agents()
        self._load_obstacles()
        self._anim = {
            "simulation_id": 1,
            "simulation_time": 20,
            "time_shift": 0,
            "animations": {
                "evacuees": [],
                "rooms_opacity": []
            }
        }
        self._run()
        self._write_zip()
        Vis({
            'highlight_geom': None,
            'anim': '1/f1.zip',
            'title': 'x',
            'srv': 1
        })

# }}}

    def _create_teleports(self):  # {{{
        '''
        TODO: non-multifloor stairacases cause the need to try/except here. 
        geom.py should handle sX.Y naming better.
        '''
        self._teleport_from = {}
        for i in self.s.query(
                "SELECT name,center_x,center_y,vent_to_name  FROM world2d WHERE vent_to_name LIKE 's%'"
        ):
            target = i['vent_to_name'].replace(".", "|")
            tt = self.s.query("SELECT x0,y0 FROM world2d WHERE name=?",
                              (target, ))
            try:
                #self._teleport_from[(i['name'], (i['center_x'], i['center_y']), i['vent_to_name'])]=(target, (tt[0]['x0'], tt[0]['y0']))
                self._teleport_from[i['name']] = (target,
                                                  (tt[0]['x0'] +
                                                   self.evacuee_radius * 2,
                                                   tt[0]['y0'] +
                                                   self.evacuee_radius * 2))
            except:
                pass
        #dd(self._teleport_from)
# }}}

    def _positions(self):  # {{{
        frame = []
        for k, v in self.agents.items():
            pos = [round(i) for i in self.sim.getAgentPosition(v['id'])]
            frame.append([pos[0], pos[1], 0, 0, "N", 1])
            #print(k,",t:", self.sim.getGlobalTime(), ",pos:", pos, ",v:", [ round(i) for i in self.sim.getAgentVelocity(v['id'])])
        self._anim["animations"]["evacuees"].append(frame)
# }}}

    def _create_agents(self):  # {{{
        door = self.s.query(
            "SELECT center_x, center_y  FROM world2d WHERE name=?",
            (self.door1, ))[0]
        z = self.s.query("SELECT * FROM world2d WHERE type_pri='EVACUEE'")
        self.agents = {}
        for i in z:
            aa = i['name']
            self.agents[aa] = {}
            ii = self.sim.addAgent((i['x0'], i['y0']))
            self.agents[aa]['name'] = aa
            self.agents[aa]['id'] = ii
            self.sim.setAgentPrefVelocity(ii, (200, 0))
            self.agents[aa]['behaviour'] = 'random'
            self.agents[aa]['target'] = (door['center_x'], door['center_y'])
# }}}

    def _load_obstacles(self):  # {{{
        obstacles = self.json.readdb('world2d_obstacles')['points']
        for i in obstacles:
            self.sim.addObstacle([(o[0], o[1]) for o in i[:4]])
            self.sim.processObstacles()
# }}}

    def _velocity(self, a):  # {{{
        x = a['target'][0] - self.sim.getAgentPosition(a['id'])[0]
        y = a['target'][1] - self.sim.getAgentPosition(a['id'])[1]
        if abs(x) + abs(y) < 30:
            self.sim.setAgentPosition(a['id'],
                                      self._teleport_from[self.door1][1])
            a['target'] = self.door2
        else:
            self.sim.setAgentPrefVelocity(a['id'], (x, y))

# }}}

    def _update(self):  # {{{
        for k, v in self.agents.items():
            self._velocity(self.agents[k])
        self._positions()
# }}}

    def _write_zip(self):  # {{{
        d = "{}/workers/1".format(os.environ['AAMKS_PROJECT'])

        zf = zipfile.ZipFile("{}/f1.zip".format(d), 'w')
        zf.writestr("anim.json", json.dumps(self._anim))
        zf.close()
# }}}

    def _run(self):  # {{{
        for t in range(100):
            self.sim.doStep()
            self._update()
예제 #14
0
class OnInit():
    def __init__(self):  # {{{
        ''' Stuff that happens at the beggining of the project '''

        if len(sys.argv) > 1: os.environ["AAMKS_PROJECT"] = sys.argv[1]
        if len(sys.argv) > 2: os.environ["AAMKS_USER_ID"] = sys.argv[2]
        self.json = Json()
        self.conf = self.json.read("{}/conf.json".format(
            os.environ['AAMKS_PROJECT']))
        self.project_id = self.conf['project_id']
        self.scenario_id = self.conf['scenario_id']
        self.p = Psql()
        self._clear_srv_anims()
        self._clear_sqlite()
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self._setup_simulations()
        self._create_sqlite_tables()
# }}}

    def _clear_srv_anims(self):  # {{{
        ''' 
        Need to detect and remove obsolete srv animations. Server always
        overwrites anims.json and we need to prevent the dumplicates in
        Animator right menu entries.

        We try: because there may be no anims.json just yet.

        TODO: it is possible we could just remove this file and remove all
        server animations. But would it hurt workers animations?
        '''

        try:
            anims = self.json.read("{}/workers/anims.json".format(
                os.environ['AAMKS_PROJECT']))
            new_anims = []
            for a in anims:
                if a['srv'] != 1:
                    new_anims.append(a)
            self.json.write(
                new_anims,
                "{}/workers/anims.json".format(os.environ['AAMKS_PROJECT']))
        except:
            pass

        try:
            os.remove("{}/workers/static.json".format(
                os.environ['AAMKS_PROJECT']))
            os.remove("{}/dd_geoms.json".format(os.environ['AAMKS_PROJECT']))
        except:
            pass
# }}}

    def _clear_sqlite(self):  # {{{
        try:
            os.remove("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        except:
            pass
# }}}

    def _create_iterations_sequence(self):  # {{{
        '''
        For a given project we may run simulation 0 to 999. Then we may wish to
        run 100 simulations more and have them numbered here: from=1000 to=1099
        These from and to numbers are unique for the project and are used as
        rand seeds in later aamks modules. This is similar, but distinct from
        SimIterations() - we are creating the sequence, and the later reads the
        sequence from/to. Remember that range(1,4) returns 1,2,3; hence SELECT
        max(iteration)+1 
        '''

        how_many = self.conf['number_of_simulations']

        r = []
        try:
            # If the project already exists in simulations table (e.g. adding 100 simulations to existing 1000); try: fails on addition on int+None.
            r.append(
                self.p.query(
                    'SELECT max(iteration)+1 FROM simulations WHERE project=%s AND scenario_id=%s',
                    (self.project_id, self.scenario_id))[0][0])
            r.append(r[0] + how_many)
        except:
            # If a new project
            r = [1, how_many + 1]
        return r
# }}}

    def _setup_simulations(self):  # {{{
        ''' Simulation dir maps to id from psql's simulations table'''

        workers_dir = "{}/workers".format(os.environ['AAMKS_PROJECT'])
        os.makedirs(workers_dir, exist_ok=True)

        irange = self._create_iterations_sequence()
        for i in range(*irange):
            sim_dir = "{}/{}".format(workers_dir, i)
            os.makedirs(sim_dir, exist_ok=True)
            self.p.query(
                "INSERT INTO simulations(iteration,project,scenario_id) VALUES(%s,%s,%s)",
                (i, self.project_id, self.scenario_id))

# }}}

    def _create_sqlite_tables(self):  # {{{
        ''' At least some tables must be create early for Vis() etc. '''

        self.s.query("CREATE TABLE dispatched_evacuees(json)")
예제 #15
0
class EvacMcarlo():
    def __init__(self):# {{{
        ''' Generate montecarlo evac.conf. '''
        self.json=Json()
        self.conf=self.json.read("{}/conf.json".format(os.environ['AAMKS_PROJECT']))
        self.s=Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.evacuee_radius=self.json.read('{}/inc.json'.format(os.environ['AAMKS_PATH']))['evacueeRadius']
        self.floors=[z['floor'] for z in self.s.query("SELECT DISTINCT floor FROM aamks_geom ORDER BY floor")]
        self._project_name=os.path.basename(os.environ['AAMKS_PROJECT'])

        si=SimIterations(self.conf['project_id'], self.conf['scenario_id'], self.conf['number_of_simulations'])
        sim_ids=range(*si.get())
        for self._sim_id in sim_ids:
            seed(self._sim_id)
            self._fire_obstacle()
            self._static_evac_conf()
            self._dispatch_evacuees()
            self._make_evac_conf()
        self._evacuees_static_animator()

# }}}
    def _static_evac_conf(self):# {{{
        ''' 
        AAMKS_PROJECT must be propagated to worker environment.
        '''

        self._evac_conf=self.conf
        self._evac_conf['AAMKS_PROJECT']=os.environ['AAMKS_PROJECT']
        self._evac_conf['SIM_ID']=self._sim_id
        self._evac_conf['SERVER']=os.environ['AAMKS_SERVER']
        self._evac_conf['FIRE_ORIGIN']=self.s.query("SELECT name FROM fire_origin WHERE sim_id=?", (self._sim_id,))[0]['name']
# }}}
    def _fire_obstacle(self):# {{{
        '''
        Fire Obstacle prevents humans to walk right through the fire. Currently
        we build the rectangle xx * yy around x,y. Perhaps this size could be
        some function of fire properties.
        '''

        xx=150
        yy=150

        z=self.s.query("SELECT * FROM fire_origin") 
        i=z[0]
        points=[ [i['x']-xx, i['y']-yy, 0], [i['x']+xx, i['y']-yy, 0], [i['x']+xx, i['y']+yy, 0], [i['x']-xx, i['y']+yy, 0], [i['x']-xx, i['y']-yy, 0] ]

        obstacles=self.json.readdb("obstacles")
        obstacles['fire']={ i['floor']: points }
        self.s.query("UPDATE obstacles SET json=?", (json.dumps(obstacles),)) 

# }}}
    def _make_pre_evacuation(self,room,type_sec):# {{{
        ''' 
        An evacuee pre_evacuates from either ordinary room or from the room of
        fire origin; type_sec is for future development.
        '''

        if room != self._evac_conf['FIRE_ORIGIN']:
            pe=self.conf['pre_evac']
        else:
            pe=self.conf['pre_evac_fire_origin']
        return round(lognorm(s=1, loc=pe['mean'], scale=pe['sd']).rvs(), 2)
# }}}
    def _get_density(self,name,type_sec,floor):# {{{
        ''' 
        Special selectors from distributions.json
        First we try to return ROOM_1_2, then ROOM_FLOOR_1, then ROOM
        Concentration comes as m^2, but aamks uses 100 * 100 cm^2 
        '''

        z=self.conf['evacuees_concentration']
        for i in [name, "{}_FLOOR_{}".format(type_sec,floor), type_sec]:
            if i in z.keys():
                return z[i] * 100 * 100
        raise Exception("Cannot determine the density for {}".format(name))

# }}}
    def _evac_rooms(self,floor): # {{{
        '''
        * probabilistic: probabilistic rooms
        * manual: manually asigned evacuees 
        '''

        rooms={}
        probabilistic_rooms={}
        for i in self.s.query("SELECT points, name, type_sec FROM aamks_geom WHERE type_pri='COMPA' AND floor=? ORDER BY global_type_id", (floor,)):
            i['points']=json.loads(i['points'])
            probabilistic_rooms[i['name']]=i

        manual_rooms={}
        for i in self.s.query("SELECT name, x0, y0 FROM aamks_geom WHERE type_pri='EVACUEE' AND floor=?", (floor,)):
            q=(floor,i['x0'], i['y0'], i['x0'], i['y0'])
            x=self.s.query("SELECT name,type_sec FROM aamks_geom WHERE type_pri='COMPA' AND floor=? AND x0<=? AND y0<=? AND x1>=? AND y1>=?", q)[0]
            if not x['name'] in manual_rooms:
                manual_rooms[x['name']]={'type_sec': x['type_sec'], 'positions': [] }
                del probabilistic_rooms[x['name']]
            manual_rooms[x['name']]['positions'].append((i['x0'], i['y0'], x['name']))

        rooms['probabilistic']=probabilistic_rooms
        rooms['manual']=manual_rooms
        return rooms
# }}}
    def _dispatch_evacuees(self):# {{{
        ''' 
        We dispatch the evacuees across the building according to the density
        distribution. 
        '''

        self.dispatched_evacuees=OrderedDict() 
        self.pre_evacuation=OrderedDict() 
        self._make_floor_obstacles()
        for floor in self.floors:
            self.pre_evacuation[floor] = list()
            positions = []
            evac_rooms=self._evac_rooms(floor)
            for name,r in evac_rooms['probabilistic'].items():
                density=self._get_density(r['name'],r['type_sec'],floor)
                room_positions=self._dispatch_inside_polygons(density,r['points'], floor, name)
                positions += room_positions
                for i in room_positions:
                    self.pre_evacuation[floor].append(self._make_pre_evacuation(r['name'], r['type_sec']))
            for name,r in evac_rooms['manual'].items():
                positions += r['positions']
                for i in r['positions']:
                    self.pre_evacuation[floor].append(self._make_pre_evacuation(name, r['type_sec']))
            self.dispatched_evacuees[floor] = positions
# }}}
    def _make_floor_obstacles(self):# {{{
        self._floor_obstacles={}
        for floor in self.floors:
            obsts=[]
            for x in self.json.readdb("obstacles")['obstacles'][floor]:
                obsts.append([(o[0],o[1]) for o in x])
            try:
                obsts.append(self.json.readdb("obstacles")['fire'][floor])
            except:
                pass
            self._floor_obstacles[floor]=unary_union([ Polygon(i) for i in obsts ])
# }}}
    def _dispatch_inside_polygons(self,density,points,floor,name):# {{{
        exterior=Polygon(points)
        exterior_minus_obsts=exterior.difference(self._floor_obstacles[floor])
        walkable=exterior_minus_obsts.buffer(- self.evacuee_radius - 10 )

        bbox=list(walkable.bounds)
        target=int(walkable.area / density)
        positions=[]
        while len(positions) < target:
            x=uniform(bbox[0], bbox[2])
            y=uniform(bbox[1], bbox[3])
            if walkable.intersects(Point(x,y)):
                positions.append((int(x),int(y), name))
        return positions
# }}}
    def _make_evac_conf(self):# {{{
        ''' Write data to sim_id/evac.json. '''

        self._evac_conf['FLOORS_DATA']=OrderedDict()
        for floor in self.floors:
            self._evac_conf['FLOORS_DATA'][floor]=OrderedDict()
            self._evac_conf['FLOORS_DATA'][floor]['NUM_OF_EVACUEES']=len(self.dispatched_evacuees[floor])
            self._evac_conf['FLOORS_DATA'][floor]['EVACUEES']=OrderedDict()
            z=self.s.query("SELECT z0 FROM aamks_geom WHERE floor=?", (floor,))[0]['z0']
            for i,pos in enumerate(self.dispatched_evacuees[floor]):
                e_id='f{}'.format(i)
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id]=OrderedDict()
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id]['ORIGIN']         = (pos[0], pos[1])
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id]['COMPA']          = (pos[2])
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id]['PRE_EVACUATION'] = self.pre_evacuation[floor][i]

                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id]['ALPHA_V']        = round(normal(self.conf['evacuees_alpha_v']['mean']     , self.conf['evacuees_alpha_v']['sd'])     , 2)
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id]['BETA_V']         = round(normal(self.conf['evacuees_beta_v']['mean']      , self.conf['evacuees_beta_v']['sd'])      , 2)
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id]['H_SPEED']        = round(normal(self.conf['evacuees_max_h_speed']['mean'] , self.conf['evacuees_max_h_speed']['sd']) , 2)
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id]['V_SPEED']        = round(normal(self.conf['evacuees_max_v_speed']['mean'] , self.conf['evacuees_max_v_speed']['sd']) , 2)

        self.json.write(self._evac_conf, "{}/workers/{}/evac.json".format(os.environ['AAMKS_PROJECT'],self._sim_id))
# }}}
    def _evacuees_static_animator(self):# {{{
        ''' 
        For the animator. We just pick a single, newest sim_id and display
        evacuees init positions. Animator can use it when there are no worker
        provided animations (moving evacuees for specific sim_id). 

        '''

        m={}
        for floor in self.floors:
            m[floor]=self.dispatched_evacuees[floor]
        self.s.query('INSERT INTO dispatched_evacuees VALUES (?)', (json.dumps(m),))
예제 #16
0
class EvacEnv:
    def __init__(self, aamks_vars):
        self.evacuees = Evacuees
        self.max_speed = 0
        self.current_time = 0
        self.positions = []
        self.velocities = []
        self.velocity_vector = []
        self.speed_vec = []
        self.fed = []
        self.fed_vec = []
        self.finished = []
        self.finished_vec = []
        self.trajectory = []
        self.focus = []
        self.smoke_query = None
        self.rset = 0
        self.per_9 = 0
        self.floor = 0
        self.nav = None
        self.room_list = OrderedDict()
        self.rooms_in_smoke = []

        f = open('{}/{}/config.json'.format(os.environ['AAMKS_PATH'], 'evac'),
                 'r')
        self.config = json.load(f)

        self.general = aamks_vars

        self.sim = rvo2.PyRVOSimulator(self.config['TIME_STEP'],
                                       self.config['NEIGHBOR_DISTANCE'],
                                       self.config['MAX_NEIGHBOR'],
                                       self.config['TIME_HORIZON'],
                                       self.config['TIME_HORIZON_OBSTACLE'],
                                       self.config['RADIUS'], self.max_speed)
        self.elog = self.general['logger']
        self.elog.info('ORCA on {} floor initiated'.format(self.floor))

    def _find_closest_exit(self, evacuee):
        '''
        It finds the closest exit from the set of available exits.
        :param evacuee: object evacuee with the defined parameters.
        :return: coordinates of the closest exit.
        '''
        paths, paths_free_of_smoke = list(), list()

        for door in self.general['doors']:
            x, y = door['center_x'], door['center_y']
            path = self.nav.query(
                [self.evacuees.get_position_of_pedestrian(evacuee), (x, y)],
                maxStraightPath=100)
            if self._next_room_in_smoke(evacuee, path) is not True:
                try:
                    paths_free_of_smoke.append([x, y, LineString(path).length])
                except:
                    self.evacuees.set_finish_to_agent(evacuee)
                    paths_free_of_smoke.append([x, y, 0])

                # paths.append([x, y, LineString(path).length])
            else:
                paths.append([x, y, LineString(path).length])

        if len(paths_free_of_smoke) > 0:
            exits = list(zip(*paths_free_of_smoke))[-1]
            return paths_free_of_smoke[exits.index(
                min(exits))][0], paths_free_of_smoke[exits.index(
                    min(exits))][1]
        else:
            exits = list(zip(*paths))[0]
            return paths[exits.index(min(exits))][0], paths[exits.index(
                min(exits))][1]

    def _next_room_in_smoke(self, evacuee, path):
        try:
            od_at_agent_position = self.smoke_query.get_visibility(
                self.evacuees.get_position_of_pedestrian(evacuee),
                self.current_time, self.floor)
        except:
            od_at_agent_position = 0, 'outside'

        self.evacuees.set_optical_density(evacuee, od_at_agent_position[0])
        self.room = od_at_agent_position[1]

        if self.config['SMOKE_AWARENESS'] and len(path) > 1:
            od_next_room = self.smoke_query.get_visibility(
                path[1], self.current_time, self.floor)
            if od_at_agent_position[0] < od_next_room[0]:
                return True
            else:
                return False
        else:
            return False

    def read_cfast_record(self, time):
        self.smoke_query.read_cfast_record(time)

    def place_evacuees(self, evacuees):
        assert isinstance(evacuees,
                          Evacuees), '%evacuees is not type of Evacuees'
        self.evacuees = evacuees
        [
            self.sim.addAgent(self.evacuees.get_origin_of_pedestrian(i))
            for (i) in range(self.evacuees.get_number_of_pedestrians())
        ]

        [
            self.sim.setAgentPrefVelocity(
                i, self.evacuees.get_velocity_of_pedestrian(i))
            for (i) in range(self.evacuees.get_number_of_pedestrians())
        ]

        [
            self.sim.setAgentMaxSpeed(
                i, self.evacuees.get_speed_max_of_pedestrian(i))
            for i in range(self.evacuees.get_number_of_pedestrians())
        ]

    @staticmethod
    def discretize_time(time):
        return int(ceil(time / 10.0)) * 10

    def get_data_for_visualization(self):
        data_row = []
        for n in range(self.sim.getNumAgents()):
            data_row.append([
                int(self.positions[n][0]),
                int(self.positions[n][1]), self.velocities[n][0],
                self.velocities[n][1], self.fed[n], self.finished[n]
            ])
        return data_row

    def update_agents_position(self):
        for i in range(self.evacuees.get_number_of_pedestrians()):
            if (self.evacuees.get_finshed_of_pedestrian(i)) == 0:
                self.sim.setAgentPosition(i, (10000 + i * 200, 10000))
                continue
            else:
                self.evacuees.set_position_to_pedestrian(
                    i, (int(self.sim.getAgentPosition(i)[0]),
                        int(self.sim.getAgentPosition(i)[1])))

        self.positions = [
            tuple((int(self.sim.getAgentPosition(i)[0]),
                   int(self.sim.getAgentPosition(i)[1])))
            for (i) in range(self.sim.getNumAgents())
        ]

    def update_agents_velocity(self):
        for i in range(self.evacuees.get_number_of_pedestrians()):
            self.evacuees.set_num_of_obstacle_neighbours(
                i, self.sim.getAgentNumObstacleNeighbors(i))
            self.evacuees.set_num_of_orca_lines(
                i, self.sim.getAgentNumORCALines(i))
            #if i == 210:
            #self.evacuees.dump_evacuee_vars(i)
            self.evacuees.calculate_pedestrian_velocity(i, self.current_time)
        for i in range(self.evacuees.get_number_of_pedestrians()):
            self.sim.setAgentPrefVelocity(
                i, self.evacuees.get_velocity_of_pedestrian(i))
        self.velocities = [
            tuple((int(self.sim.getAgentPrefVelocity(i)[0]),
                   int(self.sim.getAgentPrefVelocity(i)[1])))
            for (i) in range(self.sim.getNumAgents())
        ]

    def set_goal(self):
        for e in range(self.evacuees.get_number_of_pedestrians()):
            if (self.evacuees.get_finshed_of_pedestrian(e)) == 0:
                continue
            else:
                position = self.evacuees.get_position_of_pedestrian(e)
                goal = self.nav.query(
                    [position, self._find_closest_exit(e)], maxStraightPath=32)
                try:
                    vis = self.sim.queryVisibility(position, goal[2])
                    if vis:
                        self.evacuees.set_goal(ped_no=e, goal=goal[1:])
                    else:
                        self.evacuees.set_goal(ped_no=e, goal=goal)
                except:
                    self.evacuees.set_goal(ped_no=e, goal=goal)

        self.finished = [
            self.evacuees.get_finshed_of_pedestrian(i)
            for i in range(self.sim.getNumAgents())
        ]
        for e in range(self.sim.getNumAgents()):
            self.focus.append(self.evacuees.get_goal_of_pedestrian(e))

    def update_speed(self):
        for i in range(self.evacuees.get_number_of_pedestrians()):
            if (self.evacuees.get_finshed_of_pedestrian(i)) == 0:
                continue
            else:
                self.evacuees.update_speed_of_pedestrian(i)
                self.sim.setAgentMaxSpeed(
                    i, self.evacuees.get_speed_of_pedestrian(i))

    def update_fed(self):
        for i in range(self.evacuees.get_number_of_pedestrians()):
            if (self.evacuees.get_finshed_of_pedestrian(i)) == 0:
                continue
            else:
                try:
                    fed = self.smoke_query.get_fed(
                        self.evacuees.get_position_of_pedestrian(i),
                        self.current_time, self.floor)
                except:
                    fed = 0.0
                self.evacuees.update_fed_of_pedestrian(
                    i, fed * self.config['SMOKE_QUERY_RESOLUTION'])

        fed = [
            self.evacuees.get_fed_of_pedestrian(i)
            for i in range(self.sim.getNumAgents())
        ]
        c = None
        fed_symbilic = []
        for i in fed:
            if i < 0.01:
                c = 'N'
            elif i < 0.3:
                c = 'L'
            elif i < 1:
                c = 'M'
            else:
                c = 'H'
            fed_symbilic.append(c)
        self.fed = fed_symbilic

    def process_obstacle(self, obstacles):
        for i in range(len(obstacles)):
            obst = list()
            for n in obstacles[i]:
                obst.append(n[0:2])
            self.sim.addObstacle(obst)
        self.sim.processObstacles()
        return self.sim.getNumObstacleVertices(), 2

    def generate_nav_mesh(self):
        self.nav = Navmesh()
        self.nav.build(floor=str(self.floor))

    def prepare_rooms_list(self):
        self.s = Sqlite("aamks.sqlite")
        rooms_f = self.s.query(
            'SELECT name from aamks_geom where type_pri="COMPA" and floor = "{}"'
            .format(self.floor))
        for item in rooms_f:
            self.room_list.update({item['name']: 0.0})

    def update_room_opacity(self):
        smoke_opacity = dict()
        for room in self.room_list.keys():
            opacity = 0.0
            hgt = self.smoke_query.compa_conditions[str(room)]['HGT']
            if hgt == None:
                opacity = self._OD_to_VIS(self.smoke_query.compa_conditions[
                    str(room).split('.')[0]]['ULOD'])
            elif hgt <= self.config['LAYER_HEIGHT']:
                opacity = self._OD_to_VIS(
                    self.smoke_query.compa_conditions[str(room)]['ULOD'])
            else:
                opacity = self._OD_to_VIS(
                    self.smoke_query.compa_conditions[str(room)]['LLOD'])
            if opacity > 0.0 and room not in self.rooms_in_smoke:
                self.rooms_in_smoke.append(room)
            smoke_opacity.update({room: round(opacity, 2)})
            self.elog.debug('ROOM: {}, opacity: {}'.format(
                room, round(opacity, 2)))
        return smoke_opacity

    def _OD_to_VIS(self, OD):
        self.elog.debug('TIME: {}, optical density: {}'.format(
            self.current_time, OD))
        if OD == 0:
            return 0.0
        else:
            vis = self.general['c_const'] / log(OD)
            if vis <= 3:
                return 1.0
            elif vis >= 30:
                return 0.0
            else:
                return (30 - vis) / 30

    def do_step(self):
        self.sim.doStep()

    def get_number_of_evacuees(self):
        return self.sim.getNumAgents()

    def update_time(self):
        self.current_time += self.config['TIME_STEP']

    def get_simulation_time(self):
        return self.sim.getGlobalTime()

    def get_rset_time(self) -> None:
        exited = self.finished.count(0)
        if (exited > len(self.finished) * 0.98) and self.per_9 == 0:
            self.rset = self.current_time + 30
        if all(x == 0 for x in self.finished) and self.rset == 0:
            self.rset = self.current_time + 30

    def do_simulation(self, step):
        if (step % self.config['SMOKE_QUERY_RESOLUTION']) == 0:
            self.set_goal()
            self.update_speed()
        self.update_agents_velocity()
        self.sim.doStep()
        self.update_agents_position()
        self.update_time()
        self.elog.info(self.current_time)
        if (step % self.config['SMOKE_QUERY_RESOLUTION']) == 0:
            self.update_fed()
        self.get_rset_time()
예제 #17
0
파일: geom.py 프로젝트: kowalskiw/aamks
class Geom():
    def __init__(self):  # {{{
        self.json = Json()
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.raw_geometry = self.json.read("{}/cad.json".format(
            os.environ['AAMKS_PROJECT']))
        self.conf = self.json.read("{}/conf.json".format(
            os.environ['AAMKS_PROJECT']))
        self._doors_width = 32
        self._wall_width = 4
        self._make_elem_counter()
        self._geometry2sqlite()
        self._enhancements()
        self._init_dd_geoms()
        self._make_fake_wells()
        self._floors_details()
        self._aamks_geom_into_polygons()
        self._make_id2compa_name()
        self._find_intersections_within_floor()
        self._get_faces()
        self._hvents_per_room()
        self._find_intersections_between_floors()
        self._vvents_per_room()
        self._add_names_to_vents_from_to()
        self._calculate_sills()
        self._auto_detectors_and_sprinklers()
        self._create_obstacles()
        self.make_vis('Create obstacles')
        self._navmesh()
        self._assert_faces_ok()
        self._assert_room_has_door()
        #self.s.dumpall()
# }}}

    def _floors_details(self):  # {{{
        ''' 
        Floor dimensions are needed here and there, therefore we store it in
        sqlite. Canvas size is 1600 x 800 in css.css. Calculate how to scale
        the whole floor to fit the canvas. Minima don't have to be at (0,0) in
        autocad, therefore we also need to translate the drawing for the
        canvas. 
        '''

        values = OrderedDict()
        for floor in self.floors:
            minx = self.s.query(
                "SELECT min(x0) AS minx FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['minx']
            miny = self.s.query(
                "SELECT min(y0) AS miny FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['miny']
            maxx = self.s.query(
                "SELECT max(x1) AS maxx FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['maxx']
            maxy = self.s.query(
                "SELECT max(y1) AS maxy FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['maxy']
            z0 = self.s.query("SELECT z0 FROM aamks_geom WHERE floor=?",
                              (floor, ))[0]['z0']

            width = maxx - minx
            height = maxy - miny

            center = (minx + int(width / 2), miny + int(height / 2), z0)

            animation_scale = round(min(1600 / width, 800 / height) * 0.95,
                                    2)  # 0.95 is canvas padding
            animation_translate = [
                int(maxx - 0.5 * width),
                int(maxy - 0.5 * height)
            ]

            values[floor] = OrderedDict([('width', width), ('height', height),
                                         ('z', z0), ('center', center),
                                         ('minx', minx), ('miny', miny),
                                         ('maxx', maxx), ('maxy', maxy),
                                         ('animation_scale', animation_scale),
                                         ('animation_translate',
                                          animation_translate)])
        self.s.query("CREATE TABLE floors(json)")
        self.s.query('INSERT INTO floors VALUES (?)', (json.dumps(values), ))
# }}}

    def _geometry2sqlite(self):  # {{{
        ''' 
        Parse geometry and place geoms in sqlite. The lowest floor is always 0.
        The self.raw_geometry example for floor("0"):

            "0": [
                "ROOM": [
                    [ [ 3.0 , 4.8 , 0.0 ] , [ 4.8 , 6.5 , 3.0 ] ] ,
                    [ [ 3.0 , 6.5 , 0.0 ] , [ 6.8 , 7.4 , 3.0 ] ] 
                ]
            ]

        Each geom entity will be classified as DOOR, WINDOW, ROOM etc, and will
        get a unique name via elem_counter. Some columns in db are left empty
        for now. 

        Sqlite's aamks_geom table must use two unique ids a) 'name' for
        visualisation and b) 'global_type_id' for cfast enumeration. 
        '''

        data = []
        for floor, gg in self.raw_geometry.items():
            for k, arr in gg.items():
                for v in arr:
                    p0 = [int(i) for i in v[0]]
                    p1 = [int(i) for i in v[1]]
                    width = p1[0] - p0[0]
                    depth = p1[1] - p0[1]
                    height = p1[2] - p0[2]
                    record = self._prepare_geom_record(k, [p0, p1], width,
                                                       depth, height, floor)
                    if record != False:
                        data.append(record)
        self.s.query(
            "CREATE TABLE aamks_geom(name,floor,global_type_id,hvent_room_seq,vvent_room_seq,type_pri,type_sec,type_tri,x0,y0,z0,width,depth,height,cfast_width,sill,face,face_offset,vent_from,vent_to,material_ceiling,material_floor,material_wall,heat_detectors,smoke_detectors,sprinklers,is_vertical,vent_from_name,vent_to_name, how_much_open, room_area, x1, y1, z1, center_x, center_y, center_z, fire_model_ignore)"
        )
        self.s.executemany(
            'INSERT INTO aamks_geom VALUES ({})'.format(','.join(
                '?' * len(data[0]))), data)
        #dd(self.s.dump())
#}}}

    def _prepare_geom_record(self, k, v, width, depth, height, floor):  # {{{
        ''' Format a record for sqlite. Hvents get fixed width self._doors_width cm '''
        # OBST
        if k in ('OBST', ):
            type_pri = 'OBST'
            type_tri = ''

        # EVACUEE
        elif k in ('EVACUEE', ):
            type_pri = 'EE'
            type_tri = ''

        # MVENT
        elif k in ('MVENT', ):
            type_pri = 'MVENT'
            type_tri = ''

        # VVENT
        elif k in ('VVENT', ):
            type_pri = 'VVENT'
            height = 10
            type_tri = ''

        # COMPA
        elif k in ('ROOM', 'COR', 'HALL', 'STAI'):
            type_pri = 'COMPA'
            type_tri = ''

        # HVENT
        elif k in ('D', 'C', 'E', 'HOLE', 'W'):
            width = max(width, self._doors_width)
            depth = max(depth, self._doors_width)
            type_pri = 'HVENT'
            if k in ('D', 'C', 'E', 'HOLE'):
                type_tri = 'DOOR'
            elif k in ('W'):
                type_tri = 'WIN'

        self._elem_counter[type_pri] += 1
        global_type_id = self._elem_counter[type_pri]
        name = '{}_{}'.format(k[0], global_type_id)

        #data.append('name' , 'floor' , 'global_type_id' , 'hvent_room_seq' , 'vvent_room_seq' , 'type_pri' , 'type_sec' , 'type_tri' , 'x0'    , 'y0'    , 'z0'    , 'width' , 'depth' , 'height' , 'cfast_width' , 'sill' , 'face' , 'face_offset' , 'vent_from' , 'vent_to' , material_ceiling                      , material_floor                      , material_wall                      , 'heat_detectors' , 'smoke_detectors' , 'sprinklers' , 'is_vertical' , 'vent_from_name' , 'vent_to_name' , 'how_much_open' , 'room_area' , 'x1' , 'y1' , 'z1' , 'center_x' , 'center_y' , 'center_z' , 'fire_model_ignore')
        return (name, floor, global_type_id, None, None, type_pri, k, type_tri,
                v[0][0], v[0][1], v[0][2], width, depth, height, None, None,
                None, None, None, None, self.conf['material_ceiling']['type'],
                self.conf['material_floor']['type'],
                self.conf['material_wall']['type'], 0, 0, 0, None, None, None,
                None, None, None, None, None, None, None, None, 0)

# }}}

    def _enhancements(self):  # {{{
        ''' 
        Is HVENT vertical or horizontal? Apart from what is width and height
        for geometry, HVENTS have their cfast_width always along the wall

        Doors and Holes will be later interescted with parallel walls
        (obstacles). We inspect is_vertical and make the doors just enough
        smaller to avoid perpendicular intersections. 
        
        Since obstacles are generated to right/top direction, we need to
        address the overlapping coming from left/bottom. So we make doors
        shorter at x0 and y0, but not at x1 and y1. At the end our door=90cm
        are now 86cm. 
        '''

        self.outside_compa = self.s.query(
            "SELECT count(*) FROM aamks_geom WHERE type_pri='COMPA'"
        )[0]['count(*)'] + 1
        self.floors = [
            z['floor'] for z in self.s.query(
                "SELECT DISTINCT floor FROM aamks_geom ORDER BY floor")
        ]
        self.all_doors = [
            z['name'] for z in self.s.query(
                "SELECT name FROM aamks_geom WHERE type_tri='DOOR' ORDER BY name"
            )
        ]

        self.s.query(
            "UPDATE aamks_geom SET is_vertical=0, cfast_width=width WHERE type_pri='HVENT' AND width > depth"
        )
        self.s.query(
            "UPDATE aamks_geom SET is_vertical=1, cfast_width=depth WHERE type_pri='HVENT' AND width < depth"
        )

        self.s.query(
            "UPDATE aamks_geom SET room_area=round(width*depth/10000,2) WHERE type_pri='COMPA'"
        )
        self.s.query(
            "UPDATE aamks_geom SET x1=x0+width, y1=y0+depth, z1=z0+height, center_x=x0+width/2, center_y=y0+depth/2, center_z=z0+height/2"
        )

        self.s.query(
            "UPDATE aamks_geom SET y0=y0+?, depth=depth-? WHERE type_tri='DOOR' AND is_vertical=1",
            (self._wall_width, self._wall_width))
        self.s.query(
            "UPDATE aamks_geom SET x0=x0+?, width=width-? WHERE type_tri='DOOR' AND is_vertical=0",
            (self._wall_width, self._wall_width))

# }}}

    def _init_dd_geoms(self):  # {{{
        ''' 
        dd_geoms are some optional extra rectangles, points, lines and
        circles that are written to on top of our geoms. Useful for developing
        and debugging features. Must come early, because visualization depends
        on it. 

        Procedure:  
            * z=self.json.read('{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
            *   z["0"]['circles'].append({ "xy": (i['center_x'], i['center_y']),"radius": 200, "fillColor": "#fff" , "opacity": 0.3 } )
            *   z["0"]['circles'].append({ "xy": (i['center_x'], i['center_y']),"radius": 200, "fillColor": "#fff" , "opacity": 0.3 } )
            * self.json.write(z, '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
        '''

        z = dict()
        for floor in self.floors:
            z[floor] = dict()
            z[floor]['rectangles'] = []
            z[floor]['lines'] = []
            z[floor]['circles'] = []
            z[floor]['texts'] = []
            z[floor]['rectangles'] = []
            for i in self.s.query(
                    "SELECT * FROM aamks_geom WHERE type_tri='DOOR' AND floor=?",
                (floor, )):
                z[floor]['circles'].append({
                    "xy": (i['center_x'], i['center_y']),
                    "radius": 90,
                    "fillColor": "#fff",
                    "opacity": 0.05
                })

            # Example usage anywhere inside aamks:

            # z=self.json.read('{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
            # z["0"]['rectangles'].append( { "xy": (1000 , 1000) , "width": 200             , "depth": 300        , "strokeColor": "#fff" , "strokeWidth": 2  , "fillColor": "#f80" , "opacity": 0.7 } )
            # z["0"]['rectangles'].append( { "xy": (0    , 0)    , "width": 200              , "depth": 200        , "strokeColor": "#fff" , "strokeWidth": 2  , "fillColor": "#f80" , "opacity": 0.7 } )
            # z["0"]['lines'].append(      { "xy": (2000 , 200)  , "x1": 3400               , "y1": 500           , "strokeColor": "#fff" , "strokeWidth": 2  , "opacity": 0.7 } )
            # z["0"]['texts'].append(      { "xy": (1000 , 1000) , "content": "(1000x1000)" , "fontSize": 400      , "fillColor":"#06f"    , "opacity":0.7 } )
            # self.json.write(z, '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))

            # Vis(None, 'image', 'dd_geoms example')

        self.json.write(z,
                        '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
# }}}

    def _make_fake_wells(self):  # {{{
        ''' 
        TODO: are we using this?

        This is for evacuation only and cannot interfere with fire models
        (fire_model_ignore=1). Most STAI(RCASES) or HALL(S) are drawn on floor
        0, but they are tall and need to cross other floors. We call them
        WELLs. Say we have floor bottoms at 0, 3, 6, 9, 12 metres and WELL's
        top is at 9 metres - the WELL belongs to floors 0, 1, 2. We INSERT fake
        (x,y) WELL slices on proper floors in order to calculate vent_from /
        vent_to properly. 
        '''

        return

        add_wells = {}
        for w in self.s.query(
                "SELECT floor,global_type_id,height FROM aamks_geom WHERE type_sec in ('STAI','HALL')"
        ):
            add_wells[(w['floor'], w['global_type_id'])] = []
            current_floor = w['floor']

            for floor in self.floors:
                for v in self.s.query(
                        "SELECT min(z0) FROM aamks_geom WHERE type_pri='COMPA' AND floor=?",
                    (floor, )):
                    if v['min(z0)'] < w['height']:
                        add_wells[(w['floor'],
                                   w['global_type_id'])].append(floor)
            add_wells[(w['floor'], w['global_type_id'])].remove(current_floor)

        for w, floors in add_wells.items():
            row = self.s.query(
                "SELECT * FROM aamks_geom WHERE type_pri='COMPA' AND global_type_id=?",
                (w[1], ))[0]
            for floor in floors:
                row['fire_model_ignore'] = 1
                row['floor'] = floor
                self.s.query(
                    'INSERT INTO aamks_geom VALUES ({})'.format(','.join(
                        '?' * len(row.keys()))), list(row.values()))

# }}}

    def _make_id2compa_name(self):  # {{{
        ''' 
        Create a map of ids to names for COMPAS, e.g. _id2compa_name[4]='ROOM_1_4' 
        This map is stored to sqlite because path.py needs it. 
        Still true after mesh travelling?
        '''

        self._id2compa_name = OrderedDict()
        for v in self.s.query(
                "select name,global_type_id from aamks_geom where type_pri='COMPA' ORDER BY global_type_id"
        ):
            self._id2compa_name[v['global_type_id']] = v['name']
        self._id2compa_name[self.outside_compa] = 'outside'

# }}}

    def _add_names_to_vents_from_to(self):  # {{{
        ''' 
        Vents from/to use indices, but names will be simpler for AAMKS and for
        debugging. Hvents/Vvents lower_id/higher_id are already taken care of
        in _find_intersections_between_floors() 
        '''

        query = []
        for v in self.s.query(
                "select vent_from,vent_to,name from aamks_geom where type_pri IN('HVENT', 'VVENT')"
        ):
            query.append((self._id2compa_name[v['vent_from']],
                          self._id2compa_name[v['vent_to']], v['name']))
        self.s.executemany(
            'UPDATE aamks_geom SET vent_from_name=?, vent_to_name=? WHERE name=?',
            query)
# }}}

    def _calculate_sills(self):  # {{{
        ''' 
        Sill is the distance relative to the floor. Say there's a HVENT H
        between rooms A and B. Say A and B have variate z0 (floor elevations).
        How do we calculate the height of the sill? The CFAST.in solution is
        simple: We find 'hvent_from' for H and then we use it's z0. This z0 is
        the needed 'relative 0' for the calcuation. 
        '''

        update = []
        for v in self.s.query(
                "SELECT global_type_id, z0, vent_from  FROM aamks_geom WHERE type_pri='HVENT' ORDER BY name"
        ):
            floor_baseline = self.s.query(
                "SELECT z0 FROM aamks_geom WHERE global_type_id=? AND type_pri='COMPA'",
                (v['vent_from'], ))[0]['z0']
            update.append((v['z0'] - floor_baseline, v['global_type_id']))
        self.s.executemany(
            "UPDATE aamks_geom SET sill=? WHERE type_pri='HVENT' AND global_type_id=?",
            update)

# }}}

    def _auto_detectors_and_sprinklers(self):  # {{{
        if len(''.join([str(i)
                        for i in self.conf['heat_detectors'].values()])) > 0:
            self.s.query(
                "UPDATE aamks_geom set heat_detectors = 1 WHERE type_pri='COMPA'"
            )
        if len(''.join([str(i)
                        for i in self.conf['smoke_detectors'].values()])) > 0:
            self.s.query(
                "UPDATE aamks_geom set smoke_detectors = 1 WHERE type_pri='COMPA'"
            )
        if len(''.join([str(i)
                        for i in self.conf['sprinklers'].values()])) > 0:
            self.s.query(
                "UPDATE aamks_geom set sprinklers = 1 WHERE type_pri='COMPA'")
# }}}

    def _make_elem_counter(self):  # {{{
        ''' 
        Geom primary types are enumerated globally for the building in sqlite.
        Each of the types has separate numbering starting from 1. ROOM_2_8
        refers to the eight compartment in the building which happens to exist
        on floor 2.
        '''

        self._elem_counter = {}
        for i in ('COMPA', 'HVENT', 'VVENT', 'OBST', 'EE', 'MVENT'):
            self._elem_counter[i] = 0
# }}}

# INTERSECTIONS

    def _aamks_geom_into_polygons(self):  # {{{
        ''' aamks_geom into shapely polygons for intersections '''
        self.aamks_polies = OrderedDict()
        self.aamks_polies['COMPA'] = OrderedDict()
        self.aamks_polies['HVENT'] = OrderedDict()
        self.aamks_polies['VVENT'] = OrderedDict()
        self.aamks_polies['MVENT'] = OrderedDict()
        for floor in self.floors:
            for elem in self.aamks_polies.keys():
                self.aamks_polies[elem][floor] = OrderedDict()
            for v in self.s.query(
                    "SELECT * FROM aamks_geom WHERE type_pri NOT IN('OBST', 'EE') AND floor=?",
                (floor, )):
                self.aamks_polies[v['type_pri']][floor][
                    v['global_type_id']] = box(v['x0'], v['y0'],
                                               v['x0'] + v['width'],
                                               v['y0'] + v['depth'])
# }}}

    def _get_faces(self):  # {{{
        ''' 
        Cfast faces and offsets calculation. HVENTS have self._doors_width, so
        we only consider intersection.length > self._doors_width Faces are
        properties of doors. They are calculated in respect to the room of
        lower id. The orientation of faces in each room:

                3
            +-------+
          4 |       | 2
            +-------+
                1
        '''

        for floor in self.floors:
            for i in self.s.query(
                    "SELECT vent_from as compa_id, global_type_id as vent_id FROM aamks_geom WHERE type_pri='HVENT' AND floor=?",
                (floor, )):
                hvent_poly = self.aamks_polies['HVENT'][floor][i['vent_id']]
                compa_poly = self.aamks_polies['COMPA'][floor][i['compa_id']]
                compa = [(round(x), round(y))
                         for x, y in compa_poly.exterior.coords]
                lines = OrderedDict()
                lines[2] = LineString([compa[0], compa[1]])
                lines[3] = LineString([compa[1], compa[2]])
                lines[4] = LineString([compa[2], compa[3]])
                lines[1] = LineString([compa[3], compa[0]])
                for key, line in lines.items():
                    if hvent_poly.intersection(
                            line).length > self._doors_width:
                        pt = list(zip(*line.xy))[0]
                        face = key
                        offset = hvent_poly.distance(Point(pt))
                        self.s.query(
                            "UPDATE aamks_geom SET face=?, face_offset=? WHERE global_type_id=? AND type_pri='HVENT'",
                            (face, offset, i['vent_id']))
# }}}

    def _hvents_per_room(self):  # {{{
        '''
        If there are more than one vent in a room Cfast needs them enumerated.
        '''

        i = 0
        j = 0
        update = []
        for v in self.s.query(
                "SELECT name,vent_from,vent_to FROM aamks_geom WHERE type_pri='HVENT' ORDER BY vent_from,vent_to"
        ):
            if v['vent_from'] != i:
                i = v['vent_from']
                j = 0
            j += 1
            update.append((j, v['name']))
        self.s.executemany(
            'UPDATE aamks_geom SET hvent_room_seq=? WHERE name=?', (update))
# }}}

    def _vvents_per_room(self):  # {{{
        '''
        If there are more than one vent in a room Cfast needs them enumerated.
        '''
        i = 0
        j = 0
        update = []
        for v in self.s.query(
                "SELECT name,vent_from,vent_to FROM aamks_geom WHERE type_pri='VVENT' ORDER BY vent_from,vent_to"
        ):
            if v['vent_from'] != i:
                i = v['vent_from']
                j = 0
            j += 1
            update.append((j, v['name']))
        self.s.executemany(
            'UPDATE aamks_geom SET vvent_room_seq=? WHERE name=?', (update))
# }}}

    def _find_intersections_within_floor(self):  # {{{
        ''' 
        Find intersections (hvents vs compas). This is how we know which doors
        belong to which compas. We expect that HVENT intersects either:

            a) room_from, room_to  (two rectangles)
            b) room_from, outside  (one rectangle)

        If the door originates at the very beginning of the room, then it also
        has a tiny intersection with some third rectangle which we filter out
        with:

            intersection.length > 100 (intersection perimeter, 100 is arbitrary)

        self.aamks_polies is a dict of floors:
            COMPA: OrderedDict([(1, OrderedDict([(42, <shapely.geometry.polygon.Polygon object at 0x2b2206282e48>), (43, <shapely.geometry.polygon.Polygon object at 0x2b2206282eb8>), (44, <shapely.geometry.polygon.Polygon object at 0x2b2206282f28>)))]) ...
            HVENT: OrderedDict([(1, OrderedDict([(21, <shapely.geometry.polygon.Polygon object at 0x2b2206282fd0>), (22, <shapely.geometry.polygon.Polygon object at 0x2b2206293048>)))]) ...
            VVENT: OrderedDict([(1, OrderedDict([(1, <shapely.geometry.polygon.Polygon object at 0x2b2206298550>)]))])

        We aim at having vc_intersections (Vent_x_Compa intersections) dict of vents. Each vent collects two compas: 
            1: [48, 29]
            2: [49, 29]
            3: [11 ]    -> [11, 99(Outside)] 
            
        v=sorted(v) asserts that we go from lower_vent_id to higher_vent_id

        Also, we need to make sure room A and room B do intersect if there is door from A to B.
        '''

        update = []
        for floor, vents_dict in self.aamks_polies['HVENT'].items():
            all_hvents = [
                z['global_type_id'] for z in self.s.query(
                    "SELECT global_type_id FROM aamks_geom WHERE type_pri='HVENT' AND floor=? ORDER BY name",
                    floor)
            ]
            vc_intersections = {key: [] for key in all_hvents}
            for vent_id, vent_poly in vents_dict.items():
                for compa_id, compa_poly in self.aamks_polies['COMPA'][
                        floor].items():
                    if vent_poly.intersection(compa_poly).length > 100:
                        vc_intersections[vent_id].append(compa_id)

            for vent_id, v in vc_intersections.items():
                v = sorted(v)
                if len(v) == 2 and self.aamks_polies['COMPA'][floor][
                        v[0]].intersects(
                            self.aamks_polies['COMPA'][floor][v[1]]) == False:
                    self.make_vis("Space between compas".format(floor),
                                  vent_id)
                if len(v) == 1:
                    v.append(self.outside_compa)
                if len(v) > 2:
                    self.make_vis(
                        'Door intersects no rooms or more than 2 rooms.',
                        vent_id)
                update.append((v[0], v[1], vent_id))
        self.s.executemany(
            "UPDATE aamks_geom SET vent_from=?, vent_to=? where global_type_id=? and type_pri='HVENT'",
            update)

# }}}

    def _find_intersections_between_floors(self):  # {{{
        '''
        Similar to _find_intersections_within_floor() This time we are looking for a
        vvent (at floor n) which intersects a compa at it's floor (floor n)
        and a compa above (floor n+1) We will iterate over two_floors, which
        can contain: a) the set of compas from (floor n) and (floor n+1) b)
        the set of compas from (floor n) only if it is the top most floor --
        outside_compa will come into play

        As opposed to _find_intersections_between_floors() vents go from higher to lower:
            "UPDATE aamks_geom SET vent_to=?, vent_from=?"

            intersection.length > 100 (intersection perimeter, 100 is arbitrary)
        '''

        update = []
        for floor, vents_dict in self.aamks_polies['VVENT'].items():
            all_vvents = [
                z['global_type_id'] for z in self.s.query(
                    "SELECT global_type_id FROM aamks_geom WHERE type_pri='VVENT' AND floor=? ORDER BY name",
                    floor)
            ]
            vc_intersections = {key: [] for key in all_vvents}
            for vent_id, vent_poly in vents_dict.items():
                try:
                    two_floors = self.aamks_polies['COMPA'][
                        floor] + self.aamks_polies['COMPA'][floor + 1]
                except:
                    two_floors = self.aamks_polies['COMPA'][floor]
                for compa_id, compa_poly in two_floors.items():
                    if vent_poly.intersection(compa_poly).length > 100:
                        vc_intersections[vent_id].append(compa_id)

            for vent_id, v in vc_intersections.items():
                v = sorted(v)
                if len(v) == 1:
                    v.append(self.outside_compa)
                if len(v) > 2:
                    self.make_vis(
                        'Vent intersects no rooms or more than 2 rooms.',
                        vent_id)
                update.append((v[0], v[1], vent_id))
        self.s.executemany(
            "UPDATE aamks_geom SET vent_to=?, vent_from=? where global_type_id=? and type_pri='VVENT'",
            update)

# }}}

# OBSTACLES

    def _create_obstacles(self):  # {{{
        ''' 
        Geometry may contain obstacles to model machines, FDS walls, bookcases,
        etc. Obstacles are not visible in CFAST, since they don't belong to
        aamks_geom table. 
        '''

        data = OrderedDict()
        data['points'] = OrderedDict()
        data['named'] = OrderedDict()
        floors_meta = json.loads(
            self.s.query("SELECT json FROM floors")[0]['json'])
        for floor, gg in self.raw_geometry.items():
            data['points'][floor] = []
            data['named'][floor] = []
            boxen = []
            # TODO: once cad.json uses 'OBST': [], there's no need to try.
            try:
                for v in gg['OBST']:
                    boxen.append(
                        box(int(v[0][0]), int(v[0][1]), int(v[1][0]),
                            int(v[1][1])))
            except:
                pass

            boxen += self._rooms_into_boxen(floor)
            data['named'][floor] = self._boxen_into_rectangles(boxen)
            for i in boxen:
                data['points'][floor].append([(int(x), int(y),
                                               floors_meta[floor]['z'])
                                              for x, y in i.exterior.coords])
        self.s.query("CREATE TABLE obstacles(json)")
        self.s.query("INSERT INTO obstacles VALUES (?)", (json.dumps(data), ))
#}}}

    def _rooms_into_boxen(self, floor):  # {{{
        ''' 
        For a roomX we create a roomX_ghost, we move it by self._wall_width,
        which must match the width of hvents. Then we create walls via logical
        operations. Finally doors cut the openings in walls.

        '''

        walls = []
        for i in self.s.query(
                "SELECT * FROM aamks_geom WHERE floor=? AND type_pri='COMPA' ORDER BY name",
            (floor, )):
            walls.append((i['x0'] + self._wall_width, i['y0'],
                          i['x0'] + i['width'], i['y0'] + self._wall_width))
            walls.append((i['x0'] + i['width'], i['y0'],
                          i['x0'] + i['width'] + self._wall_width,
                          i['y0'] + i['depth'] + self._wall_width))
            walls.append((i['x0'] + self._wall_width, i['y0'] + i['depth'],
                          i['x0'] + i['width'],
                          i['y0'] + i['depth'] + self._wall_width))
            walls.append((i['x0'], i['y0'], i['x0'] + self._wall_width,
                          i['y0'] + i['depth'] + self._wall_width))
        walls_polygons = ([
            box(ii[0], ii[1], ii[2], ii[3]) for ii in set(walls)
        ])

        doors_polygons = []
        for i in self.s.query(
                "SELECT * FROM aamks_geom WHERE floor=? AND type_tri='DOOR' ORDER BY name",
            (floor, )):
            doors_polygons.append(
                box(i['x0'], i['y0'], i['x0'] + i['width'],
                    i['y0'] + i['depth']))

        boxen = []
        for wall in walls_polygons:
            for door in doors_polygons:
                wall = wall.difference(door)
            if isinstance(wall, MultiPolygon):
                for i in polygonize(wall):
                    boxen.append(i)
            elif isinstance(wall, Polygon):
                boxen.append(wall)
        return boxen
# }}}

    def _boxen_into_rectangles(self, boxen):  # {{{
        ''' 
        Transform shapely boxen into rectangles for paperjs visualization:
            [(x0,y0,width,height)]
        '''

        rectangles = []

        for i in boxen:
            m = i.bounds
            coords = OrderedDict()
            coords["x0"] = int(m[0])
            coords["y0"] = int(m[1])
            coords["width"] = int(m[2] - m[0])
            coords["depth"] = int(m[3] - m[1])
            rectangles.append(coords)

        return rectangles
# }}}

# NAVMESH

    def _navmesh(self):  # {{{
        ''' 
        1. Create obj file from aamks geometries.
        2. Build navmesh with golang, obj is input
        3. Query navmesh with python
        '''

        self.nav = OrderedDict()
        z = self.s.query("SELECT json FROM obstacles")
        for floor, faces in json.loads(z[0]['json'])['points'].items():
            self._obj_num = 0
            obj = ''
            for face in faces:
                obj += self._obj_elem(face, 99)
            for face in self._obj_platform(floor):
                obj += self._obj_elem(face, 0)

            with open("{}/{}.obj".format(os.environ['AAMKS_PROJECT'], floor),
                      "w") as f:
                f.write(obj)
            self.nav[floor] = Navmesh()
            self.nav[floor].build(obj, os.environ['AAMKS_PROJECT'], floor)
            self._navmesh_test(floor)

        Vis({
            'highlight_geom': None,
            'anim': None,
            'title': 'Navmesh test',
            'srv': 1
        })

# }}}

    def _obj_platform(self, floor):  # {{{
        z = self.s.query(
            "SELECT x0,y0,x1,y1 FROM aamks_geom WHERE type_pri='COMPA' AND floor=?",
            (floor, ))
        platforms = []
        for i in z:
            platforms.append([(i['x1'], i['y1']), (i['x1'], i['y0']),
                              (i['x0'], i['y0']), (i['x0'], i['y1'])])
        return platforms

# }}}

    def _obj_elem(self, face, z):  # {{{
        elem = ''
        elem += "o Face{}\n".format(self._obj_num)
        for verts in face[:4]:
            elem += "v {}\n".format(" ".join(
                [str(i / 100) for i in [verts[0], z, verts[1]]]))
        elem += "f {}\n\n".format(" ".join(
            [str(4 * self._obj_num + i) + "//1" for i in [1, 2, 3, 4]]))
        self._obj_num += 1
        return elem
# }}}

    def _navmesh_test(self, floor):  # {{{
        colors = ["#fff", "#f80", "#f00", "#8f0", "#08f", "#f0f"]
        navmesh_paths = []

        for x in range(6):
            src_dest = []
            for i in self.s.query(
                    "SELECT * FROM aamks_geom WHERE type_pri='COMPA' AND floor=? ORDER BY RANDOM() LIMIT 2",
                (floor, )):
                src_dest.append([
                    round(uniform(i['x0'], i['x1'])),
                    round(uniform(i['y0'], i['y1']))
                ])

            z = self.json.read('{}/dd_geoms.json'.format(
                os.environ['AAMKS_PROJECT']))
            z[floor]['circles'].append({
                "xy": (src_dest[0]),
                "radius": 20,
                "fillColor": colors[x],
                "opacity": 1
            })
            z[floor]['circles'].append({
                "xy": (src_dest[1]),
                "radius": 20,
                "fillColor": colors[x],
                "opacity": 1
            })
            self.json.write(
                z, '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
            navmesh_paths.append(self.nav[floor].query(src_dest, floor))
        self._navmesh_vis(floor, navmesh_paths, colors)
# }}}

    def _navmesh_vis(self, floor, navmesh_paths, colors):  # {{{
        j = Json()
        z = j.read('{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
        for cc, path in enumerate(navmesh_paths):
            for i, p in enumerate(path):
                try:
                    z[floor]['lines'].append({
                        "xy": (path[i][0], path[i][1]),
                        "x1": path[i + 1][0],
                        "y1": path[i + 1][1],
                        "strokeColor": colors[cc],
                        "strokeWidth": 2,
                        "opacity": 0.7
                    })
                except:
                    pass

        j.write(z, '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
# }}}

# ASSERTIONS

    def _assert_faces_ok(self):  # {{{
        ''' Are all hvents' faces fine? '''
        for v in self.s.query(
                "SELECT * FROM aamks_geom WHERE type_tri='DOOR' ORDER BY vent_from,vent_to"
        ):
            assert v['face_offset'] is not None, self.make_vis(
                'Problem with cfast face calculation.', v['global_type_id'])
# }}}

    def _assert_room_has_door(self):  # {{{
        ''' Each room must have at least one type_tri DOOR. '''
        doors_intersect_room_ids = []
        for i in self.s.query(
                "SELECT vent_from,vent_to FROM aamks_geom WHERE type_tri='DOOR'"
        ):
            doors_intersect_room_ids.append(i['vent_from'])
            doors_intersect_room_ids.append(i['vent_to'])

        all_interected_room = set(doors_intersect_room_ids)
        for i in self.s.query(
                "SELECT name,floor,global_type_id FROM aamks_geom WHERE type_pri='COMPA'"
        ):
            if i['global_type_id'] not in all_interected_room:
                self.make_vis('Room without door (see Animator)',
                              i['global_type_id'], 'COMPA')
# }}}

    def make_vis(self, title, faulty_id='', type_pri='HVENT'):  # {{{
        ''' 
        This method is for visualizing both errors and just how things look. 
        If faulty_id comes non-empty then we are signaling an error.
        '''

        if faulty_id != '':
            r = self.s.query(
                "SELECT name,floor FROM aamks_geom WHERE type_pri=? AND global_type_id=?",
                (type_pri, faulty_id))[0]
            fatal = "Fatal: {}: {}".format(r['name'], title)
            Vis({
                'highlight_geom': r['name'],
                'anim': None,
                'title': "<div id=python_msg>{}</div>".format(fatal),
                'srv': 1
            })
            print(fatal)
            sys.exit()
        else:
            Vis({
                'highlight_geom': None,
                'anim': None,
                'title': title,
                'srv': 1
            })
예제 #18
0
class Worker:

    def __init__(self):
        self.start_time = time.time()
        self.url = sys.argv[1]
        self.vars = OrderedDict()
        self.results = dict()
        self.obstacles = None
        self.trajectory = None
        self.velocity = None
        self.floor_dims = None
        self.evacuees = None
        self.fire_dto = None
        self.sim_floors = None
        self.start_time = time.time()
        self.floors = list()
        self.host_name = os.uname()[1]
        os.chdir('/home/aamks_users')
        os.environ["AAMKS_PROJECT"]='.'
        self.working_dir = self.url.split('aamks_users/')[1]
        self.cross_building_results = None
        self.simulation_time = None
        self.time_shift = None
        self.animation_data = []
        self.smoke_opacity = []
        self.rooms_in_smoke = dict()

    def get_logger(self, logger_name):
        FORMATTER = logging.Formatter("%(asctime)s — %(name)s — %(levelname)s — %(message)s")
        LOG_FILE = "/tmp/aamks_{}.log".format(self.sim_id)
        file_handler = TimedRotatingFileHandler(LOG_FILE, when='midnight')
        file_handler.setFormatter(FORMATTER)
        logger = logging.getLogger(logger_name)
        logger.setLevel(eval('logging.{}'.format(self.config['LOGGING_MODE'])))
        logger.addHandler(file_handler)
        logger.propagate = False
        return logger

    def get_config(self):
        try:
            f = open('{}/{}/config.json'.format(os.environ['AAMKS_PATH'], 'evac'), 'r')
            self.config = json.load(f)
        except Exception as e:
            print(e)
            sys.exit(1)

        try:
            self.vars['conf'] = json.loads(urlopen('{}/evac.json'.format(self.url)).read().decode())
        except Exception as e:
            print('Cannot fetch evac.json from server: {}'.format(str(e)))
            sys.exit(1)
        else:
            print('URL OK')

        self.sim_id = self.vars['conf']['SIM_ID']
        self.host_name = os.uname()[1]
        self.db_server = self.vars['conf']['SERVER']
        print('Starting simulations id: {}'.format(self.sim_id))
        self.wlogger=self.get_logger('worker.py')
        self.vars['conf']['logger'] = self.get_logger('evac.py')

    def get_geom_and_cfast(self):

        os.chdir(self.working_dir)

        self.wlogger.info('URL: {}'.format(self.url))

        try:
            urlretrieve('{}/../../aamks.sqlite'.format(self.url), 'aamks.sqlite')

        except Exception as e:
            self.wlogger.error(e)
        else:
            self.wlogger.debug('Aamks.sqlite fetched from server')

        try:
            self.cfast_input = urlopen('{}/cfast.in'.format(self.url)).read().decode()
        except Exception as e:
            self.wlogger.error(e)
        else:
            self.wlogger.debug('cfast.in fetched from server')

        self.wlogger.info("Host: {} start simulation id: {}".format(self.host_name, self.sim_id))

    def _create_workspace(self):
        try:
            shutil.rmtree(self.working_dir, ignore_errors=True)
            os.makedirs(self.working_dir)
        except Exception as e:
            self.wlogger.error(e)
        else:
            self.wlogger.debug('Workspace created')

    def run_cfast_simulations(self):

        try:
            with open('cfast.in', "w") as f:
                f.write(self.cfast_input)
        except Exception as e:
            self.wlogger.error(e)
        else:
            self.wlogger.debug('cfast.in saved')

        try:
            os.system('/usr/local/aamks/fire/cfast cfast.in')
        except Exception as e:
            self.wlogger.error(e)
            cfast_log = open('cfast.log', 'r')
            for line in cfast_log.readlines():
                if line.startswith("***Error:"):
                    self.wlogger.error(Exception(line))
        else:
            self.wlogger.info('CFAST simulation calculated with success')

    def create_geom_database(self):

        self.s = Sqlite("aamks.sqlite")
        #self.report_log_issue(message=self.s.dumpall(), mode='DEBUG')
        doors = self.s.query('SELECT floor, name, center_x, center_y from aamks_geom where type_pri="HVENT" AND vent_to_name="outside"')
        self.vars['conf']['doors']=doors
        self.obstacles = json.loads(self.s.query('SELECT * FROM obstacles')[0]['json'], object_pairs_hook=OrderedDict)
        self.wlogger.info('SQLite load successfully')

    def _create_evacuees(self, floor):

        evacuees = []
        self.wlogger.debug('Adding evacuues on floor: {}'.format(floor))

        floor = self.vars['conf']['FLOORS_DATA'][str(floor)]

        for i in floor['EVACUEES'].keys():
            evacuees.append(Evacuee(origin=tuple(floor['EVACUEES'][i]['ORIGIN']), v_speed=floor['EVACUEES'][i]['V_SPEED'],
                                    h_speed=floor['EVACUEES'][i]['H_SPEED'], pre_evacuation=floor['EVACUEES'][i]['PRE_EVACUATION'],
                                    alpha_v=floor['EVACUEES'][i]['ALPHA_V'], beta_v=floor['EVACUEES'][i]['BETA_V'],
                                    node_radius=self.config['NODE_RADIUS']))
            self.wlogger.debug('{} evacuee added'.format(i))

        e = Evacuees()
        [e.add_pedestrian(i) for i in evacuees]

        self.wlogger.info('Num of evacuees placed: {}'.format(len(evacuees)))
        return e

    def prepare_simulations(self):
        obstacles = []
        eenv = None

        for i in self.obstacles['obstacles'].keys():
            try:
                eenv = EvacEnv(self.vars['conf'])
                eenv.floor = i
            except Exception as e:
                self.wlogger.error(e)
            else:
                self.wlogger.info('rvo2_dto ready on {} floors'.format(i))

            for obst in self.obstacles['obstacles'][str(i)]:
                obstacles.append([tuple(x) for x in array(obst)[[0,1,2,3,4,1]]])
            eenv.obstacle = obstacles
            num_of_vertices = eenv.process_obstacle(obstacles)
            eenv.generate_nav_mesh()
            self.wlogger.debug('Added obstacles on floor: {}, number of vercites: {}'.format(1, num_of_vertices))

            e = self._create_evacuees(i)
            self.wlogger.info('Evacuees placed on floor: {}'.format(i))
            eenv.place_evacuees(e)
            eenv.prepare_rooms_list()
            self.wlogger.info('Room list prepared on floor: {}'.format(i))
            self.floors.append(eenv)


    def connect_rvo2_with_smoke_query(self):

        for i in self.floors:
            try:
                i.smoke_query = SmokeQuery(floor=i.floor)
            except Exception as e:
                self.wlogger.error(e)
            else:
                self.wlogger.info('Smoke query connected to floor: {}'.format(i.floor))

    def do_simulation(self):
        self.wlogger.info('Starting simulations')
        time_frame = 10
        first_evacuue = []
        while 1:
            self.floors[0].smoke_query.cfast_has_time(time_frame)
            if self.floors[0].smoke_query.cfast_has_time(time_frame) == 1:
                self.wlogger.info('Simulation time: {}'.format(time_frame))
                rsets = []
                for i in self.floors:
                    i.read_cfast_record(time_frame)
                    first_evacuue.append(i.evacuees.get_first_evacuees_time())

                for step in range(0, int(10 / self.floors[0].config['TIME_STEP'])):
                    time_row = dict()
                    smoke_row = dict()
                    for i in self.floors:
                        i.do_simulation(step)
                        if (step % i.config['VISUALIZATION_RESOLUTION']) == 0:
                            time_row.update({str(i.floor): i.get_data_for_visualization()})
                            smoke_row.update({str(i.floor): i.update_room_opacity()})
                    if len(time_row) > 0:
                        self.animation_data.append(time_row)
                        self.smoke_opacity.append(smoke_row)

                for i in self.floors:
                    rsets.append(i.rset)
                    self.rooms_in_smoke.update({i.floor: i.rooms_in_smoke})
                time_frame += 10
            else:
                time.sleep(1)
            self.wlogger.info('Progress: {}%'.format(round(time_frame/self.vars['conf']['simulation_time'] * 100), 1))
            if time_frame > (self.vars['conf']['simulation_time'] - 10):
                self.wlogger.info('Simulation ends due to user time limit: {}'.format(self.vars['conf']['simulation_time']))
                break
            if prod(array(rsets)) > 0:
                self.wlogger.info('Simulation ends due to successful evacuation: {}'.format(rsets))
                self.simulation_time = max(rsets)
                self.time_shift = 0
                break
        self.cross_building_results = self.floors[0].smoke_query.get_final_vars()
        self.wlogger.info('Final results gathered')
        self.wlogger.debug('Final results gathered: {}'.format(self.cross_building_results))



    def send_report(self): # {{{
        '''
        Runs on a worker. Write /home/aamks/project/sim_id.json on each aRun
        completion. Then inform gearman server to scp to itself
        /home/aamks/project/sim_id.json via aOut service. Gearman server will
        process this json via /usr/local/aamks/manager/results_collector.py.
        Gearman server will psql insert and will scp the worker's animation to
        itself.
        '''
        self._write_animation_zips()
        self._write_meta()

        Popen("gearman -h {} -f aOut '{} {} {}'".format(os.environ['AAMKS_SERVER'], self.host_name, '/home/aamks_users/'+self.working_dir+'/'+self.meta_file, self.sim_id), shell=True)
        self.wlogger.info('aOut launched successfully')
    # }}}
    def _write_animation_zips(self):# {{{
        '''
        Raw data comes as an argument. We create /home/aamks/1.anim.zip
        with anim.json inside.
        '''

        '''Selecting only the rooms that had smoke during simulations'''
        smoke_data = []
        for row in self.smoke_opacity:
            floors = dict()
            for key in row.keys():
                room_on_floor = dict()
                for room in self.rooms_in_smoke[key]:
                    room_on_floor.update({room: row[key][room]})
                floors.update({key: room_on_floor})
            smoke_data.append(floors)
        self.wlogger.info('Smoke data created')

        json_content = {
                        'simulation_id': self.sim_id,
                        'simulation_time': self.simulation_time,
                        'time_shift': self.time_shift,
                        'animations': {
                            'evacuees': self.animation_data,
                            'rooms_opacity': smoke_data
                        }
                        }
        zf = zipfile.ZipFile("{}_{}_{}_anim.zip".format(self.vars['conf']['project_id'], self.vars['conf']['scenario_id'], self.sim_id), mode='w', compression=zipfile.ZIP_DEFLATED)
        try:
            zf.writestr("anim.json", json.dumps(json_content))
            self.wlogger.info('Date for animation saved')
        finally:
            zf.close()

    # }}}
    def _write_meta(self):# {{{
        j=Json()
        report = OrderedDict()
        report['worker'] = self.host_name
        report['sim_id'] = self.sim_id
        report['scenario_id'] = self.vars['conf']['scenario_id']
        report['project_id'] = self.vars['conf']['project_id']
        report['path_to_project'] = '/home/aamks_users/'+self.working_dir.split('workers')[0]
        report['fire_origin'] = self.vars['conf']['FIRE_ORIGIN']
        report['highlight_geom'] = None
        report['psql'] = dict()
        report['psql']['fed'] = dict()
        report['psql']['rset'] = dict()
        report['psql']['runtime'] = int(time.time() - self.start_time)
        report['psql']['cross_building_results'] = self.cross_building_results
        for i in self.floors:
            report['psql']['fed'][i.floor] = i.fed
            report['psql']['rset'][i.floor] = int(i.rset)
        for num_floor in range(len(self.floors)):
            report['animation'] = "{}_{}_{}_anim.zip".format(self.vars['conf']['project_id'], self.vars['conf']['scenario_id'], self.sim_id)
            report['floor'] = num_floor

        self.meta_file = "meta_{}.json".format(self.sim_id)
        j.write(report, self.meta_file)
        self.wlogger.info('Metadata prepared successfully')
    # }}}

    def main(self):
        self.get_config()
        self._create_workspace()
        self.get_geom_and_cfast()
        self.create_geom_database()
        self.run_cfast_simulations()
        self.prepare_simulations()
        self.connect_rvo2_with_smoke_query()
        self.do_simulation()
        self.send_report()
        self.wlogger.info('Simulation ended')

    def test(self):
        self.get_config()
        self.get_geom_and_cfast()
        self.create_geom_database()
        self.prepare_simulations()
        self.connect_rvo2_with_smoke_query()
        self.do_simulation()
        self.send_report()

    def local_worker(self):
        self.get_config()
        self.get_geom_and_cfast()
        self.create_geom_database()
        self.run_cfast_simulations()
        self.prepare_simulations()
        self.connect_rvo2_with_smoke_query()
        self.do_simulation()
예제 #19
0
class Obstacles():
    def __init__(self):# {{{
        self.json=Json()
        self.conf=self.json.read("{}/conf.json".format(os.environ['AAMKS_PROJECT']))
        self.fire_model=self.conf['fire_model'];
        self.s=Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.world_meta=self.json.readdb("world_meta")
        self.floors_meta=self.json.readdb("floors_meta")
        self.floors=self.floors_meta.keys()
        self.walls_width=self.world_meta['walls_width']
        self._create_obstacles('aamks_geom', 'obstacles')
        # TODO: in future we will probably process vertical staircases outside of aamks_geoms db table
        #if self.world_meta['multifloor_building']==1:
        #    self._create_obstacles('world2d', 'world2d_obstacles')
        #exit()

# }}}
    def _create_obstacles(self, tin, tout):# {{{
        ''' 
        Geometry may contain obstacles for modeling machines, FDS walls, bookcases,
        etc. Obstacles are not visible in CFAST.
        '''

        data=OrderedDict()
        for r in self.s.query("SELECT DISTINCT(floor) FROM {}".format(tin)):
            floor=r['floor']
            zz=0
            if tin=='aamks_geom':
                zz=self.floors_meta[floor]['minz_abs']
            data[floor]=[]
            obsts=[]
            for o in self.s.query("SELECT points FROM {} WHERE type_pri='OBST' AND floor=?".format(tin), (floor,)):
                obsts.append(Polygon(json.loads(o['points'])))
                
            obsts+=self._floor2obsts(tin,floor)
            for i in obsts:
                data[floor].append([(int(x),int(y), zz) for x,y in i.exterior.coords])
        self.s.query("CREATE TABLE {} (json)".format(tout))
        self.s.query("INSERT INTO {} VALUES (?)".format(tout), (json.dumps({'obstacles': data}),))
        #self.s.dumpall()
#}}}
    def _floor2obsts(self,tin,floor):# {{{
        ''' 
        For a roomX we create a roomX_ghost, we move it by self.walls_width,
        which must match the width of hvents. Then we create walls via logical
        operations. Finally doors cut the openings in walls.

        '''
        if self.fire_model=='FDS':
            return []

        walls=[]
        for i in self.s.query("SELECT * FROM {} WHERE floor=? AND type_pri='COMPA' ORDER BY name".format(tin), (floor,)):

            walls.append((i['x0']+self.walls_width , i['y0']            , i['x0']+i['width']                  , i['y0']+self.walls_width)                )
            walls.append((i['x0']+i['width']       , i['y0']            , i['x0']+i['width']+self.walls_width , i['y0']+i['depth']+self.walls_width)     )
            walls.append((i['x0']+self.walls_width , i['y0']+i['depth'] , i['x0']+i['width']                  , i['y0']+i['depth']+self.walls_width)     )
            walls.append((i['x0']                  , i['y0']            , i['x0']+self.walls_width            , i['y0']+i['depth']+self.walls_width)     )

        walls_polygons=([box(ii[0],ii[1],ii[2],ii[3]) for ii in set(walls)])

        doors_polygons=[]
        for i in self.s.query("SELECT * FROM {} WHERE floor=? AND type_tri='DOOR' ORDER BY name".format(tin), (floor,)):
            doors_polygons.append(box(i['x0'], i['y0'], i['x0']+i['width'], i['y0']+i['depth']))
            
        obsts=[]
        for wall in walls_polygons:
            for door in doors_polygons:
                wall=wall.difference(door)
            if isinstance(wall, MultiPolygon):
                for i in polygonize(wall):
                    obsts.append(i)
            elif isinstance(wall, Polygon):
                obsts.append(wall)
        return obsts 
예제 #20
0
class EvacEnv:
    def __init__(self):  # {{{
        self.json = Json()
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))

        self.evacuee_radius = self.json.read("{}/inc.json".format(
            os.environ['AAMKS_PATH']))['evacueeRadius']
        time = 1
        self.sim = rvo2.PyRVOSimulator(time, 40, 5, time, time,
                                       self.evacuee_radius, 30)
        self.make_nav("0")
        self._anim = {
            "simulation_id": 1,
            "simulation_time": 20,
            "time_shift": 0,
            "animations": {
                "evacuees": [],
                "rooms_opacity": []
            }
        }
        self._create_agents()
        self._load_obstacles()
        self._run()
        self._write_zip()
        Vis({
            'highlight_geom': None,
            'anim': '1/f1.zip',
            'title': 'x',
            'srv': 1
        })

# }}}

    def make_nav(self, floor):  # {{{
        self.nav = Navmesh()
        self.nav.build(floor)
# }}}

    def _create_agents(self):  # {{{
        z = self.s.query("SELECT * FROM aamks_geom WHERE type_pri='EVACUEE'")
        self.agents = {}
        for i in z:
            aa = i['name']
            self.agents[aa] = {}
            ii = self.sim.addAgent((i['x0'], i['y0']))
            self.agents[aa]['name'] = aa
            self.agents[aa]['id'] = ii
            self.sim.setAgentPrefVelocity(ii, (0, 0))
            self.agents[aa]['behaviour'] = 'random'
            self.agents[aa]['origin'] = (i['x0'], i['y0'])
            self.agents[aa]['target'] = self.nav.room_leaves(
                (i['x0'], i['y0']))['best'][0]
        self._positions()
# }}}

    def _load_obstacles(self):  # {{{
        z = self.json.readdb('obstacles')
        obstacles = z['obstacles']['0']
        for i in obstacles:
            self.sim.addObstacle([(o[0], o[1]) for o in i[:4]])
            self.sim.processObstacles()
# }}}

    def _velocity(self, a):  # {{{
        '''
        radius=3.5 is the condition for the agent to reach the behind-doors target 
        '''

        dx = a['target'][0] - self.sim.getAgentPosition(a['id'])[0]
        dy = a['target'][1] - self.sim.getAgentPosition(a['id'])[1]
        self.sim.setAgentPrefVelocity(a['id'], (dx, dy))
        return sqrt(dx**2 + dy**2)

# }}}

    def _positions(self):  # {{{
        frame = []
        for k, v in self.agents.items():
            pos = [round(i) for i in self.sim.getAgentPosition(v['id'])]
            frame.append([pos[0], pos[1], 0, 0, "N", 1])
        self._anim["animations"]["evacuees"].append({"0": frame})
# }}}

    def _update(self):  # {{{
        for k, v in self.agents.items():
            target_dist = self._velocity(self.agents[k])
            if target_dist <= self.evacuee_radius * 3.5:
                #dd(self.agents[k]['id'], target_dist)
                pass
                #exit()

        self._positions()
# }}}

    def _write_zip(self):  # {{{
        d = "{}/workers/1".format(os.environ['AAMKS_PROJECT'])

        zf = zipfile.ZipFile("{}/f1.zip".format(d), 'w')
        zf.writestr("anim.json", json.dumps(self._anim))
        zf.close()
# }}}

    def _run(self):  # {{{
        for t in range(100):
            self.sim.doStep()
            self._update()
예제 #21
0
    class ResultsCollector():
        def __init__(self, host, meta_file, sim_id):  # {{{
            '''
            1. aamksrun makes gearman pass these jobs to workers: 
                /usr/local/aamks/tests/worker.py
            2. Worker calls gearman server aOut function
            3. This file implements gearman's aOut function:
                * download results.json with configuration to workers/123/report_123.json
                * download animation.zip to workers/123/anim.zip
            '''
            self.host = host
            self.meta_file = meta_file
            self.sim_id = sim_id
            self.meta = None

            self.json = Json()
            self._fetch_meta()
            self._animation()
            self.psql_report()
# }}}

        def _fetch_meta(self):  # {{{
            SendMessage("self.meta copied")
            try:
                Popen([
                    "scp", "{}:{}".format(self.host, self.meta_file),
                    self.meta_file
                ]).wait()
            except Exception as e:
                SendMessage(e)
            else:
                pass

            self.meta = self.json.read(self.meta_file)
# }}}

        def _fire_origin_coords(self, sim_id):  # {{{
            room = self.meta['fire_origin']

            self.s = Sqlite("{}/aamks.sqlite".format(
                self.meta['path_to_project']))
            z = self.s.query(
                "SELECT center_x, center_y FROM aamks_geom WHERE name=?",
                (room, ))[0]
            return (z['center_x'], z['center_y'])
# }}}

        def _animation(self):  # {{{
            source = self.host + ':' + self.meta[
                'path_to_project'] + 'workers/' + str(
                    self.meta['sim_id']) + '/' + self.meta['animation']
            dest = self.meta['path_to_project'] + 'workers/' + str(
                self.meta['sim_id']) + '/' + self.meta['animation']
            Popen(["scp", source, dest])

            self.jsonOut = OrderedDict()
            self.jsonOut['sort_id'] = int(sim_id)
            self.jsonOut['title'] = "sim{}, f{}".format(
                self.meta['sim_id'], self.meta['floor'])
            self.jsonOut['floor'] = self.meta['floor']
            self.jsonOut['fire_origin'] = self._fire_origin_coords(
                self.meta['sim_id'])
            self.jsonOut['highlight_geom'] = None
            self.jsonOut['anim'] = "{}/{}".format(self.meta['sim_id'],
                                                  self.meta['animation'])

            anims_master = "{}/workers/vis/anims.json".format(
                self.meta['path_to_project'])
            try:
                z = self.json.read(anims_master)
            except:
                z = []
            z.append(self.jsonOut)
            self.json.write(z, anims_master)
# }}}

        def psql_report(self):
            p = Psql()
            fed = json.dumps(self.meta['psql']['fed'])
            rset = json.dumps(self.meta['psql']['rset'])
            p.query(
                "UPDATE simulations SET fed = '{}', wcbe='{}', run_time = {}, dcbe_time = {}, min_vis_compa = {}, max_temp = {}, host = '{}', min_hgt_compa = {}, min_vis_cor = {}, min_hgt_cor = {} WHERE project=%s AND iteration=%s"
                .format(
                    fed, rset, self.meta['psql']['runtime'],
                    self.meta['psql']['cross_building_results']['dcbe'],
                    self.meta['psql']['cross_building_results']
                    ['min_vis_compa'], self.meta['psql']
                    ['cross_building_results']['max_temp_compa'],
                    self.meta['worker'], self.meta['psql']
                    ['cross_building_results']['min_hgt_compa'],
                    self.meta['psql']['cross_building_results']['min_vis_cor'],
                    self.meta['psql']['cross_building_results']
                    ['min_hgt_cor']),
                (self.meta['project_id'], self.meta['sim_id']))
예제 #22
0
class Navmesh: 
    def __init__(self):# {{{
        ''' 
        installer/navmesh_installer.sh installs all the dependencies.

        * navmesh build from the obj geometry file
        thanks to https://github.com/arl/go-detour !

        * navmesh query
        thanks to https://github.com/layzerar/recastlib/ !

        ============================================

        This is how we are supposed to be called:

        nav=Navmesh()
        nav.build(floor)
        nav.nav_query(src, dst)

            or if you want to block r1 and r2 and have the navmeshes named

        navs=dict()
        navs[('r1','r2')]=Navmesh()
        navs[('r1','r2')].build(floor,('r1','r2'))
        navs[('r1','r2')].nav_query(src, dst)

        '''

        self.json=Json()
        self.s=Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self._test_colors=[ "#f80", "#f00", "#8f0", "#08f" ]
        self.navmesh=OrderedDict()
        self.partition_query={}
        self.evacuee_radius=self.json.read('{}/inc.json'.format(os.environ['AAMKS_PATH']))['evacueeRadius']
# }}}

    def build(self,floor,bypass_rooms=[]):# {{{
        self.floor=floor
        self.bypass_rooms=bypass_rooms
        self._get_name(bypass_rooms)
        file_obj=self._obj_make(bypass_rooms)
        file_nav="{}/{}".format(os.environ['AAMKS_PROJECT'], self.nav_name)
        file_conf="{}/recast.yml".format(os.environ['AAMKS_PROJECT'])
        with open(file_conf, "w") as f: 
            f.write('''\
            cellsize: 0.10
            cellheight: 0.2
            agentheight: 2
            agentradius: 0.30
            agentmaxclimb: 0.1
            agentmaxslope: 45
            regionminsize: 8
            regionmergesize: 20
            edgemaxlen: 12
            edgemaxerror: 1.3
            vertsperpoly: 6
            detailsampledist: 6
            detailsamplemaxerror: 1
            partitiontype: 1
            tilesize: 0
            ''')
        subprocess.call("rm -rf {}; recast --config {} --input {} build {} 1>/dev/null 2>/dev/null".format(file_nav, file_conf, file_obj, file_nav), shell=True)

        try:
            self.NAV = dt.dtLoadSampleTileMesh(file_nav)
        except:
            raise SystemExit("Navmesh: cannot create {}".format(file_nav))
# }}}
    def nav_query(self,src,dst,maxStraightPath=16):# {{{
        '''
        ./Detour/Include/DetourNavMeshQuery.h: maxStraightPath: The maximum number of points the straight path arrays can hold.  [Limit: > 0]
        We set maxStraightPath to a default low value which stops calculations early
        If one needs to get the full path to the destination one must call us with any high value, e.g. 999999999


        '''

        filtr = dt.dtQueryFilter()
        query = dt.dtNavMeshQuery()

        status = query.init(self.NAV, 2048)
        if dt.dtStatusFailed(status):
            return "err", -1, status

        polyPickExt = dt.dtVec3(2.0, 4.0, 2.0)
        startPos = dt.dtVec3(src[0]/100, 1, src[1]/100)
        endPos = dt.dtVec3(dst[0]/100, 1, dst[1]/100)

        status, out = query.findNearestPoly(startPos, polyPickExt, filtr)
        if dt.dtStatusFailed(status):
            return "err", -2, status
        startRef = out["nearestRef"]
        _startPt = out["nearestPt"]

        status, out = query.findNearestPoly(endPos, polyPickExt, filtr)
        if dt.dtStatusFailed(status):
            return "err", -3, status
        endRef = out["nearestRef"]
        _endPt = out["nearestPt"]

        status, out = query.findPath(startRef, endRef, startPos, endPos, filtr, maxStraightPath)
        if dt.dtStatusFailed(status):
            return "err", -4, status
        pathRefs = out["path"]

        status, fixEndPos = query.closestPointOnPoly(pathRefs[-1], endPos)
        if dt.dtStatusFailed(status):
            return "err", -5, status

        status, out = query.findStraightPath(startPos, fixEndPos, pathRefs, maxStraightPath, 0)
        if dt.dtStatusFailed(status):
            return "err", -6, status
        straightPath = out["straightPath"]
        straightPathFlags = out["straightPathFlags"]
        straightPathRefs = out["straightPathRefs"]
        
        path=[]
        for i in straightPath:
            path.append((i[0]*100, i[2]*100))

        if path[0]=="err":
            return None
        else :
            return path
# }}}
    def closest_terminal(self,p0,exit_type):# {{{
        '''
        The shortest polyline defines the closest exit from the floor. 
        dist < 10 test asserts the polyline has min 2 distinct points.

        exit_type: primary | secondary | any
        '''

        if exit_type in ['primary', 'secondary']:
            r=self.s.query("SELECT name,center_x,center_y FROM aamks_geom WHERE terminal_door=? AND floor=?", (exit_type, self.floor))
        else:
            r=self.s.query("SELECT name,center_x,center_y FROM aamks_geom WHERE terminal_door IS NOT NULL AND floor=?", (self.floor,))
        m={}
        closest={ 'len': 999999999, 'name': None, 'x': None, 'y': None }
        for i in r:
            if abs(i['center_x']-p0[0]) < 10 and abs(i['center_y']-p0[1]) < 10: 
                closest={ 'name': i['name'],  'x': i['center_x'], 'y': i['center_y'],'len': 0  }
                return closest
            ll=self.path_length(p0,(i['center_x'],i['center_y']))
            if ll < closest['len']:
                closest={ 'name': i['name'], 'x': i['center_x'], 'y': i['center_y'], 'len': int(ll) }
        return closest
            
# }}}
    def room_leaves(self,ee):# {{{
        self.partition_query[self.floor]=PartitionQuery(self.floor)
        room=self.partition_query[self.floor].xy2room(ee)
        closest={ 'len': 999999999, 'name': None, 'x': None, 'y': None }

        leaves={}
        for door in self._room_exit_doors(room):
            dest=self._move_dest_around_door({'e': ee, 'door': door, 'room': room})
            ll=self.path_length(ee,dest)
            leaves[ll]=(dest, door['name'])

        best_point, best_name=leaves[min(leaves.keys())]
        best_path=self.nav_query(ee, best_point)
        candidates={'best_point': best_point, 'best_name': best_name, 'best_path': best_path, 'all': list(leaves.values())}
        return candidates

# }}}
    def path_length(self, src, dst):# {{{
        return LineString(self.nav_query(src, dst, 300)).length 
# }}}
    def nav_plot_path(self,points,ddgeoms,style={}):# {{{
        ss={ "strokeColor": "#fff", "strokeWidth": 14  , "opacity": 0.5 }
        for m,n in style.items():
            ss[m]=n
        ddgeoms.add({'floor': self.floor, 'type': 'path', "g": { "points": points } , 'style': ss } )
# }}}
    def test(self):# {{{
        self.conf=self.json.read("{}/conf.json".format(os.environ['AAMKS_PROJECT']))
        agents_pairs=4
        ee=self.s.query("SELECT name,x0,y0 FROM aamks_geom WHERE type_pri='EVACUEE' AND floor=? ORDER BY global_type_id LIMIT ?", (self.floor, agents_pairs*2))
        if len(ee) == 0: return
        evacuees=list(self._chunks(ee,2))
        self._test_evacuees_pairs(evacuees)
        if self.conf['fire_model'] != 'FDS':
            self._test_room_leaves((ee[0]['x0'], ee[0]['y0']))
        Vis({'highlight_geom': None, 'anim': None, 'title': 'Nav {} test'.format(self.nav_name), 'srv': 1})

# }}}

    def _bricked_wall(self, bypass_rooms=[]):# {{{
        '''
        For navmesh we may wish to turn some doors into bricks.
        '''

        bricked_wall=[]

        if len(bypass_rooms) > 0 :
            floors_meta=json.loads(self.s.query("SELECT json FROM floors_meta")[0]['json'])
            elevation=floors_meta[self.floor]['minz_abs']
            where=" WHERE "
            where+=" vent_from_name="+" OR vent_from_name=".join([ "'{}'".format(i) for i in bypass_rooms])
            where+=" OR vent_to_name="+" OR vent_to_name=".join([ "'{}'".format(i) for i in bypass_rooms])
            bypass_doors=self.s.query("SELECT name,x0,y0,x1,y1 FROM aamks_geom {}".format(where))

            for i in bypass_doors:
                bricked_wall.append([[i['x0'],i['y0'],elevation], [i['x1'],i['y0'],elevation], [i['x1'],i['y1'],elevation], [i['x0'],i['y1'],elevation], [i['x0'],i['y0'],elevation]])

        bricked_wall+=self.json.readdb("obstacles")['obstacles'][self.floor]
        try:
            bricked_wall.append(self.json.readdb("obstacles")['fire'][self.floor])
        except:
            pass

        return bricked_wall

# }}}
    def _obj_platform(self):# {{{
        z=self.s.query("SELECT x0,y0,x1,y1 FROM aamks_geom WHERE type_pri='COMPA' AND floor=?", (self.floor,))
        platforms=[]
        for i in z:
            platforms.append([ (i['x1'], i['y1']), (i['x1'], i['y0']), (i['x0'], i['y0']), (i['x0'], i['y1']) ])
        return platforms

# }}}
    def _obj_elem(self,face,z):# {{{
        elem=''
        elem+="o Face{}\n".format(self._obj_num)
        for verts in face[:4]:
            elem+="v {}\n".format(" ".join([ str(i/100) for i in [verts[0], z, verts[1]]]))
        elem+="f {}\n\n".format(" ".join([ str(4*self._obj_num+i)+"//1" for i in [1,2,3,4]]))
        self._obj_num+=1
        return elem
# }}}
    def _obj_make(self,bypass_rooms):# {{{
        ''' 
        1. Create obj file from aamks geometries.
        2. Build navmesh with golang, obj is input
        3. Query navmesh with python
        4. bypass_rooms are the rooms excluded from navigation

        99 is the z-dim in cm

        '''

        z=self._bricked_wall(bypass_rooms)
        obj='';
        self._obj_num=0;
        for face in z:
            obj+=self._obj_elem(face,99)
        for face in self._obj_platform():
            obj+=self._obj_elem(face,0)
        
        path="{}/{}.obj".format(os.environ['AAMKS_PROJECT'], self.nav_name)
        with open(path, "w") as f: 
            f.write(obj)
        return path

# }}}
    def _chunks(self,l, n):# {{{
        """Yield successive n-sized chunks from l."""
        for i in range(0, len(l), n):
            yield l[i:i + n]
# }}}
    def _get_name(self,bypass_rooms=[]):# {{{
        brooms=''
        if len(bypass_rooms)>0:
            brooms="-"+"-".join(bypass_rooms)
        self.nav_name="{}{}.nav".format(self.floor,brooms)

# }}}

    def _hole_connected_rooms(self): # {{{
        ''' 
        room A may be hole-connected to room B which may be hole-connnected to
        room C and so on -- recursion
        '''

        if self._hole_count == len(self._hole_rooms):
            return 
        else:
            self._hole_count=len(self._hole_rooms)
            tt=copy.deepcopy(self._hole_rooms)
            for room in self._hole_rooms.keys():
                for i in self.s.query("SELECT vent_from_name,vent_to_name FROM aamks_geom WHERE type_sec='HOLE' AND (vent_from_name=? OR vent_to_name=?)", (room, room)):
                    tt[i['vent_from_name']]=1
                    tt[i['vent_to_name']]=1
            self._hole_rooms=copy.deepcopy(tt)
            return self._hole_connected_rooms()
# }}}
    def _room_exit_doors(self,room): # {{{
        ''' 
        An agents is found in room A. If room A is connected to room B via a
        hole, then we are looking for way out via doors in the "merged" AB
        room. 

        room A may be hole-connected to room B which may be hole-connnected to
        room C and so on, hence recursive _hole_connected_rooms() need to
        calculate _hole_rooms
        '''

        self._hole_rooms={room: 1}
        self._hole_count=0
        self._hole_connected_rooms()
        doors={}
        for rr in self._hole_rooms.keys():
            z=self.s.query("SELECT name, type_sec, x0, y0, x1, y1, center_x, center_y, is_vertical FROM aamks_geom WHERE type_sec='DOOR' AND (vent_from_name=? OR vent_to_name=?)", (rr, rr))
            for d in z:
                doors[d['name']]=d
        return list(doors.values())
# }}}
    def _move_dest_around_door(self,data):# {{{
        '''
        For the hole-connnected rooms the dest point must be calculated
        relative to the ROOM (inside, outside). It won't do relative to the
        EVACUEE position (left, right, above, below). 
        '''
        top_right=self.partition_query[self.floor].xy2room([data['door']['x1'], data['door']['y0']])

        #print(data['door']['name'], top_right, [data['door']['x1'], data['door']['y0']])
        if data['door']['is_vertical']==1:
            if top_right in self._hole_rooms.keys():
                dest=(data['door']['center_x'] - self.evacuee_radius*4, data['door']['center_y'])
            else:
                dest=(data['door']['center_x'] + self.evacuee_radius*4, data['door']['center_y'])
        else:
            if top_right in self._hole_rooms.keys():
                dest=(data['door']['center_x'], data['door']['center_y'] + self.evacuee_radius*4)
            else:
                dest=(data['door']['center_x'], data['door']['center_y'] - self.evacuee_radius*4)

        return dest
# }}}

    def _test_evacuees_pairs(self,evacuees):# {{{
        ddgeoms=DDgeoms()
        ddgeoms.open()
        for x,i in enumerate(evacuees):
            src=(i[0]['x0'], i[0]['y0'])
            dst=(i[1]['x0'], i[1]['y0'])
            color=self._test_colors[x] if x <= len(self._test_colors) else "#fff"
            ddgeoms.add({'floor': self.floor, 'type': 'circle', "g": { 'p0': src, "radius": 30 }, "style": { "fillColor": color , "opacity": 1 }})
            ddgeoms.add({'floor': self.floor, 'type': 'circle', "g": { 'p0': dst, "radius": 30 }, "style": { "fillColor": color , "opacity": 1 }})
            self.nav_plot_path(self.nav_query(src, dst, 300),ddgeoms,style={ 'strokeColor': color } )
        ddgeoms.write()
# }}}
    def _test_room_leaves(self,ee):# {{{
        ''' 
        radius=3.5 is the condition for the agent to reach the behind-doors target 
        '''

        ddgeoms=DDgeoms()
        ddgeoms.open()

        mm=self.room_leaves(ee)
        for dest in mm['all']:
            ddgeoms.add({'floor': self.floor, 'type': 'circle', "g": { "p0": dest[0], "radius": self.evacuee_radius*3.5 }, "style": { "fillColor": "#f80", "opacity": 0.3 }} )
            ddgeoms.add({'floor': self.floor, 'type': 'circle', "g": { "p0": dest[0], "radius": self.evacuee_radius*0.5 }, "style": { "fillColor": "#f80", "opacity": 0.5 }} )

        ddgeoms.add({'floor': self.floor, 'type': 'circle', "g": { "p0": mm['best_point'], "radius": self.evacuee_radius*3.5 }, "style": { "fillColor": "#f80", "opacity": 0.3 }} )
        ddgeoms.add({'floor': self.floor, 'type': 'circle', "g": { "p0": mm['best_point'], "radius": self.evacuee_radius*0.5 }, "style": { "fillColor": "#f80", "opacity": 0.5 }} )

        self.nav_plot_path(mm['best_path'], ddgeoms, style={ "strokeColor": "#f80", "strokeWidth": 6 } )
        ddgeoms.write()
예제 #23
0
class CFASTimporter():
    def __init__(self):  # {{{
        self.json = Json()
        self.conf = self.json.read("{}/conf.json".format(
            os.environ['AAMKS_PROJECT']))
        if self.conf['fire_model'] == 'FDS':
            return
        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.raw_geometry = self.json.read("{}/cad.json".format(
            os.environ['AAMKS_PROJECT']))
        self.geomsMap = self.json.read("{}/inc.json".format(
            os.environ['AAMKS_PATH']))['aamksGeomsMap']
        self.doors_width = 32
        self.walls_width = 4
        self._geometry2sqlite()
        self._enhancements()
        self._init_dd_geoms()
        self._towers_slices()
        self._floors_meta()
        self._world_meta()
        self._aamks_geom_into_polygons()
        self._make_id2compa_name()
        self._find_intersections_within_floor()
        self._get_faces()
        self._hvents_per_room()
        self._find_intersections_between_floors()
        self._vvents_per_room()
        self._add_names_to_vents_from_to()
        self._recalculate_vents_from_to()
        self._calculate_sills()
        self._terminal_doors()
        self._auto_detectors_and_sprinklers()
        self._assert_faces_ok()
        self._assert_room_has_door()
        self._debug()
# }}}

    def _floors_meta(self):  # {{{
        ''' 
        Floor dimensions are needed here and there. 
        for z_absolute high towers are not taken under account, we want the natural floor's zdim 
        for z_relative high towers are not taken under account, we want the natural floor's zdim 
        '''

        self.floors_meta = OrderedDict()
        self._world3d = dict()
        self._world3d['minx'] = 9999999
        self._world3d['maxx'] = -9999999
        self._world3d['miny'] = 9999999
        self._world3d['maxy'] = -9999999
        prev_maxz = 0
        for floor in self.floors:
            ty = 0
            tx = 0
            minx = self.s.query(
                "SELECT min(x0) AS minx FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['minx']
            maxx = self.s.query(
                "SELECT max(x1) AS maxx FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['maxx']
            miny = self.s.query(
                "SELECT min(y0) AS miny FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['miny']
            maxy = self.s.query(
                "SELECT max(y1) AS maxy FROM aamks_geom WHERE floor=?",
                (floor, ))[0]['maxy']
            minz_abs = self.s.query(
                "SELECT min(z0) AS minz FROM aamks_geom WHERE type_sec NOT IN('STAI','HALL') AND floor=?",
                (floor, ))[0]['minz']
            maxz_abs = self.s.query(
                "SELECT max(z1) AS maxz FROM aamks_geom WHERE type_sec NOT IN('STAI','HALL') AND floor=?",
                (floor, ))[0]['maxz']
            zdim = maxz_abs - prev_maxz
            prev_maxz = maxz_abs

            xdim = maxx - minx
            ydim = maxy - miny
            center = (minx + int(xdim / 2), miny + int(ydim / 2), minz_abs)
            self.floors_meta[floor] = OrderedDict([('name', floor),
                                                   ('xdim', xdim),
                                                   ('ydim', ydim),
                                                   ('center', center),
                                                   ('minx', minx),
                                                   ('miny', miny),
                                                   ('maxx', maxx),
                                                   ('maxy', maxy),
                                                   ('minz_abs', minz_abs),
                                                   ('maxz_abs', maxz_abs),
                                                   ('zdim', zdim), ('ty', ty),
                                                   ('tx', tx)])

            self._world3d['minx'] = min(self._world3d['minx'], minx)
            self._world3d['maxx'] = max(self._world3d['maxx'], maxx)
            self._world3d['miny'] = min(self._world3d['miny'], miny)
            self._world3d['maxy'] = max(self._world3d['maxy'], maxy)

        self.s.query("CREATE TABLE floors_meta(json)")
        self.s.query('INSERT INTO floors_meta VALUES (?)',
                     (json.dumps(self.floors_meta), ))

# }}}

    def _world_meta(self):  # {{{
        self.s.query("CREATE TABLE world_meta(json)")
        self.world_meta = {}
        self.world_meta['world3d'] = self._world3d
        self.world_meta['walls_width'] = self.walls_width
        self.world_meta['doors_width'] = self.doors_width

        if len(self.floors_meta) > 1:
            self.world_meta['multifloor_building'] = 1
        else:
            self.world_meta['multifloor_building'] = 0

        self.s.query('INSERT INTO world_meta(json) VALUES (?)',
                     (json.dumps(self.world_meta), ))

# }}}

    def _geometry2sqlite(self):  # {{{
        ''' 
        Parse geometry and place geoms in sqlite. The lowest floor is always 0.
        The self.raw_geometry example for floor("0"):

            "0": [
                "ROOM": [
                    [ [ 3.0 , 4.8 , 0.0 ] , [ 4.8 , 6.5 , 3.0 ] ] ,
                    [ [ 3.0 , 6.5 , 0.0 ] , [ 6.8 , 7.4 , 3.0 ] ] 
                ]
            ]

        Some columns in db are left empty for now. 

        Sqlite's aamks_geom table must use two unique ids 
        a) 'name' for visualisation and 
        b) 'global_type_id' for cfast enumeration. 
        '''

        data = []
        for floor, gg in self.raw_geometry.items():
            for k, arr in gg.items():
                if k in ('UNDERLAY'):
                    continue
                for v in arr:
                    rr = {
                        'x0': int(v[0][0]),
                        'y0': int(v[0][1]),
                        'x1': int(v[1][0]),
                        'y1': int(v[1][1]),
                        'z0': int(v[0][2]),
                        'z1': int(v[1][2])
                    }
                    points = ((rr['x0'], rr['y0']), (rr['x1'], rr['y0']),
                              (rr['x1'], rr['y1']), (rr['x0'], rr['y1']),
                              (rr['x0'], rr['y0']))
                    width = rr['x1'] - rr['x0']
                    depth = rr['y1'] - rr['y0']
                    height = rr['z1'] - rr['z0']
                    attrs = self._prepare_attrs(v[2])
                    record = self._prepare_geom_record(k, rr, points, width,
                                                       depth, height, floor,
                                                       attrs)
                    if record != False:
                        data.append(record)
        self.s.query(
            "CREATE TABLE aamks_geom(name,floor,global_type_id,hvent_room_seq,vvent_room_seq,type_pri,type_sec,type_tri,x0,y0,z0,width,depth,height,cfast_width,sill,face,face_offset,vent_from,vent_to,material_ceiling,material_floor,material_wall,heat_detectors,smoke_detectors,sprinklers,is_vertical,vent_from_name,vent_to_name, how_much_open, room_area, x1, y1, z1, center_x, center_y, center_z, fire_model_ignore, mvent_throughput, exit_type, room_enter, terminal_door, points)"
        )
        self.s.executemany(
            'INSERT INTO aamks_geom VALUES ({})'.format(','.join(
                '?' * len(data[0]))), data)
        #dd(self.s.dump())
#}}}

    def _prepare_attrs(self, attrs):  # {{{
        aa = {"mvent_throughput": None, "exit_type": None, "room_enter": None}
        for k, v in attrs.items():
            aa[k] = v
        return aa
# }}}

    def _prepare_geom_record(self, k, rect, points, width, depth, height,
                             floor, attrs):  # {{{
        ''' Format a record for sqlite. Hvents get fixed width self.doors_width cm '''
        # OBST
        if k in ('OBST', ):
            type_pri = 'OBST'
            type_tri = ''

        # EVACUEE
        elif k in ('EVACUEE', ):
            type_pri = 'EVACUEE'
            type_tri = ''

        # FIRE
        elif k in ('FIRE', ):
            type_pri = 'FIRE'
            type_tri = ''

        # MVENT
        elif k in ('MVENT', ):
            type_pri = 'MVENT'
            type_tri = ''

        # VVENT
        elif k in ('VVENT', ):
            type_pri = 'VVENT'
            height = 10
            type_tri = ''

        # COMPA
        elif k in ('ROOM', 'COR', 'HALL', 'STAI'):
            type_pri = 'COMPA'
            type_tri = ''

        # HVENT
        elif k in ('DOOR', 'DCLOSER', 'DELECTR', 'HOLE', 'WIN'):
            width = max(width, self.doors_width)
            depth = max(depth, self.doors_width)
            type_pri = 'HVENT'
            if k in ('DOOR', 'DCLOSER', 'DELECTR', 'HOLE'):
                type_tri = 'DOOR'
            elif k in ('WIN'):
                type_tri = 'WIN'

        global_type_id = attrs['idx']
        name = '{}{}'.format(self.geomsMap[k], global_type_id)

        #self.s.query("CREATE TABLE aamks_geom(name , floor , global_type_id , hvent_room_seq , vvent_room_seq , type_pri , type_sec , type_tri , x0         , y0         , z0         , width , depth , height , cfast_width , sill , face , face_offset , vent_from , vent_to , material_ceiling                      , material_floor                      , material_wall                      , heat_detectors , smoke_detectors , sprinklers , is_vertical , vent_from_name , vent_to_name , how_much_open , room_area , x1   , y1   , z1   , center_x , center_y , center_z , fire_model_ignore , mvent_throughput          , exit_type          , room_enter          , terminal_door , points)")
        return (name, floor, global_type_id, None, None, type_pri, k, type_tri,
                rect['x0'], rect['y0'], rect['z0'], width, depth, height, None,
                None, None, None, None, None,
                self.conf['material_ceiling']['type'],
                self.conf['material_floor']['type'],
                self.conf['material_wall']['type'], 0, 0, 0, None, None, None,
                None, None, None, None, None, None, None, None, 0,
                attrs['mvent_throughput'], attrs['exit_type'],
                attrs['room_enter'], None, json.dumps(points))

# }}}

    def _enhancements(self):  # {{{
        ''' 
        Is HVENT vertical or horizontal? Apart from what is width and height
        for geometry, HVENTS have their cfast_width always along the wall

        Doors and Holes will be later interescted with parallel walls
        (obstacles). We inspect is_vertical and make the doors just enough
        smaller to avoid perpendicular intersections. 
        
        Since obstacles are generated to right/top direction, we need to
        address the overlapping coming from left/bottom. So we make doors
        shorter at x0 and y0, but not at x1 and y1. At the end our door=90cm
        are now 86cm. 
        '''

        self.outside_compa = self.s.query(
            "SELECT count(*) FROM aamks_geom WHERE type_pri='COMPA'"
        )[0]['count(*)'] + 1
        self.floors = [
            z['floor'] for z in self.s.query(
                "SELECT DISTINCT floor FROM aamks_geom ORDER BY floor")
        ]
        self.all_doors = [
            z['name'] for z in self.s.query(
                "SELECT name FROM aamks_geom WHERE type_tri='DOOR' ORDER BY name"
            )
        ]

        self.s.query(
            "UPDATE aamks_geom SET is_vertical=0, cfast_width=width WHERE type_pri='HVENT' AND width > depth"
        )
        self.s.query(
            "UPDATE aamks_geom SET is_vertical=1, cfast_width=depth WHERE type_pri='HVENT' AND width < depth"
        )

        self.s.query(
            "UPDATE aamks_geom SET room_area=width*depth WHERE type_pri='COMPA'"
        )
        self.s.query(
            "UPDATE aamks_geom SET x1=x0+width, y1=y0+depth, z1=z0+height, center_x=x0+width/2, center_y=y0+depth/2, center_z=z0+height/2"
        )

        self.s.query(
            "UPDATE aamks_geom SET y0=y0+?, depth=depth-? WHERE type_tri='DOOR' AND is_vertical=1",
            (self.walls_width, self.walls_width))
        self.s.query(
            "UPDATE aamks_geom SET x0=x0+?, width=width-? WHERE type_tri='DOOR' AND is_vertical=0",
            (self.walls_width, self.walls_width))

# }}}

    def _init_dd_geoms(self):  # {{{
        ''' 
        dd_geoms are some optional extra rectangles, points, lines and
        circles that are written to on top of our geoms. Useful for developing
        and debugging features. Must come early, because visualization depends
        on it. 

        Procedure:  
            z=self.json.read('{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
            z["0"]['rectangles'].append( { "xy": (0    , 0)    , "width": 200              , "depth": 200        , "strokeColor": "#fff" , "strokeWidth": 2  , "fillColor": "#f80" , "opacity": 0.7 } )
            z["0"]['circles'].append({ "xy": (i['center_x'], i['center_y']),"radius": 200, "fillColor": "#fff" , "opacity": 0.3 } )
            self.json.write(z, '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
        '''

        z = dict()
        for floor in self.floors:
            z[floor] = dict()
            z[floor]['rectangles'] = []
            z[floor]['lines'] = []
            z[floor]['circles'] = []
            z[floor]['texts'] = []
            z[floor]['rectangles'] = []
            #for i in self.s.query("SELECT * FROM aamks_geom WHERE type_tri='DOOR' AND floor=?", (floor,)):
            #    z[floor]['circles'].append({ "xy": (i['center_x'] , i['center_y']) , "radius": 90 , "fillColor": "#fff" , "opacity": 0.05 } )

            # Example usage anywhere inside aamks:

            # z=self.json.read('{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
            # z["0"]['rectangles'].append( { "xy": (1000 , 1000) , "width": 200             , "depth": 300        , "strokeColor": "#fff" , "strokeWidth": 2  , "fillColor": "#f80" , "opacity": 0.7 } )
            # z["0"]['rectangles'].append( { "xy": (0    , 0)    , "width": 200              , "depth": 200        , "strokeColor": "#fff" , "strokeWidth": 2  , "fillColor": "#f80" , "opacity": 0.7 } )
            # z["0"]['lines'].append(      { "xy": (2000 , 200)  , "x1": 3400               , "y1": 500           , "strokeColor": "#fff" , "strokeWidth": 2  , "opacity": 0.7 } )
            # z["0"]['texts'].append(      { "xy": (1000 , 1000) , "content": "(1000x1000)" , "fontSize": 400      , "fillColor":"#06f"    , "opacity":0.7 } )
            # self.json.write(z, '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))

            # Vis(None, 'image', 'dd_geoms example')

        self.json.write(z,
                        '{}/dd_geoms.json'.format(os.environ['AAMKS_PROJECT']))
# }}}

    def _make_id2compa_name(self):  # {{{
        ''' 
        Create a map of ids to names for COMPAS, e.g. _id2compa_name[4]='ROOM_1_4' 
        This map is stored to sqlite because path.py needs it. 
        Still true after mesh travelling?
        '''

        self._id2compa_name = OrderedDict()
        for v in self.s.query(
                "select name,global_type_id from aamks_geom where type_pri='COMPA' ORDER BY global_type_id"
        ):
            self._id2compa_name[v['global_type_id']] = v['name']
        self._id2compa_name[self.outside_compa] = 'outside'

# }}}

    def _add_names_to_vents_from_to(self):  # {{{
        ''' 
        Vents from/to use indices, but names will be simpler for AAMKS and for
        debugging. Hvents/Vvents lower_id/higher_id are already taken care of
        in _find_intersections_between_floors() 
        '''

        query = []
        for v in self.s.query(
                "select vent_from,vent_to,name from aamks_geom where type_pri IN('HVENT', 'VVENT')"
        ):
            query.append((self._id2compa_name[v['vent_from']],
                          self._id2compa_name[v['vent_to']], v['name']))
        self.s.executemany(
            'UPDATE aamks_geom SET vent_from_name=?, vent_to_name=? WHERE name=?',
            query)
# }}}

    def _recalculate_vents_from_to(self):  # {{{
        ''' 
        CFAST requires that towers slices are mapped back to the original cuboids
        '''

        update = []
        for hi, lo in self.towers_parents.items():
            z = self.s.query(
                "SELECT name,vent_from FROM aamks_geom WHERE type_pri='HVENT' AND vent_from=? OR vent_to=? ORDER BY name",
                (hi, hi))
            for i in z:
                update.append(
                    (min(lo, i['vent_from']), max(lo,
                                                  i['vent_from']), i['name']))
        self.s.executemany(
            "UPDATE aamks_geom SET vent_from=?, vent_to=?  WHERE name=?",
            update)

# }}}

    def _calculate_sills(self):  # {{{
        ''' 
        Sill is the distance relative to the floor. Say there's a HVENT H
        between rooms A and B. Say A and B may have variate z0 (floor elevations).
        We find 'hvent_from' and then we use it's z0. This z0 is the needed
        'relative 0' for the calcuation. 
        '''

        update = []
        for v in self.s.query(
                "SELECT global_type_id, z0, vent_from  FROM aamks_geom WHERE type_pri='HVENT' ORDER BY name"
        ):
            floor_baseline = self.s.query(
                "SELECT z0 FROM aamks_geom WHERE global_type_id=? AND type_pri='COMPA'",
                (v['vent_from'], ))[0]['z0']
            update.append((v['z0'] - floor_baseline, v['global_type_id']))
        self.s.executemany(
            "UPDATE aamks_geom SET sill=? WHERE type_pri='HVENT' AND global_type_id=?",
            update)

# }}}

    def _terminal_doors(self):  # {{{
        ''' 
        Doors that lead to outside or lead to staircases are terminal
        '''
        terminal_rooms = self.s.query(
            "SELECT global_type_id FROM aamks_geom WHERE type_sec='STAI'")

        update = []
        for i in terminal_rooms:
            z = self.s.query(
                "SELECT name,exit_type FROM aamks_geom WHERE type_pri='HVENT' AND (vent_from=? OR vent_to=? OR vent_to_name='outside')",
                (i['global_type_id'], i['global_type_id']))
            for ii in z:
                update.append((ii['exit_type'], ii['name']))
        self.s.executemany(
            "UPDATE aamks_geom SET terminal_door=? WHERE name=?", update)
        #dd(self.s.query("SELECT name,terminal_door,vent_from_name,vent_to_name from aamks_geom order by name"))

# }}}

    def _auto_detectors_and_sprinklers(self):  # {{{
        if len(''.join([str(i)
                        for i in self.conf['heat_detectors'].values()])) > 0:
            self.s.query(
                "UPDATE aamks_geom set heat_detectors = 1 WHERE type_pri='COMPA'"
            )
        if len(''.join([str(i)
                        for i in self.conf['smoke_detectors'].values()])) > 0:
            self.s.query(
                "UPDATE aamks_geom set smoke_detectors = 1 WHERE type_pri='COMPA'"
            )
        if len(''.join([str(i)
                        for i in self.conf['sprinklers'].values()])) > 0:
            self.s.query(
                "UPDATE aamks_geom set sprinklers = 1 WHERE type_pri='COMPA'")
# }}}

# INTERSECTIONS

    def _aamks_geom_into_polygons(self):  # {{{
        ''' aamks_geom into shapely polygons for intersections '''
        self.aamks_polies = OrderedDict()
        self.aamks_polies['COMPA'] = OrderedDict()
        self.aamks_polies['HVENT'] = OrderedDict()
        self.aamks_polies['VVENT'] = OrderedDict()
        self.aamks_polies['MVENT'] = OrderedDict()
        for floor in self.floors:
            for elem in self.aamks_polies.keys():
                self.aamks_polies[elem][floor] = OrderedDict()
            for v in self.s.query(
                    "SELECT * FROM aamks_geom WHERE type_pri NOT IN('OBST', 'EVACUEE', 'FIRE') AND floor=?",
                (floor, )):
                self.aamks_polies[v['type_pri']][floor][
                    v['global_type_id']] = box(v['x0'], v['y0'],
                                               v['x0'] + v['width'],
                                               v['y0'] + v['depth'])
# }}}

    def _get_faces(self):  # {{{
        ''' 
        Cfast faces and offsets calculation. HVENTS have self.doors_width, so
        we only consider intersection.length > self.doors_width. Faces are
        properties of the doors. They are calculated in respect to the room of
        lower id. The orientation of faces in each room:

                3
            +-------+
          4 |       | 2
            +-------+
                1
        '''

        for floor in self.floors:
            for i in self.s.query(
                    "SELECT vent_from as compa_id, global_type_id as vent_id FROM aamks_geom WHERE type_pri='HVENT' AND floor=?",
                (floor, )):
                hvent_poly = self.aamks_polies['HVENT'][floor][i['vent_id']]
                compa_poly = self.aamks_polies['COMPA'][floor][i['compa_id']]
                compa = [(round(x), round(y))
                         for x, y in compa_poly.exterior.coords]
                lines = OrderedDict()
                lines[2] = LineString([compa[0], compa[1]])
                lines[3] = LineString([compa[1], compa[2]])
                lines[4] = LineString([compa[2], compa[3]])
                lines[1] = LineString([compa[3], compa[0]])
                for key, line in lines.items():
                    if hvent_poly.intersection(line).length > self.doors_width:
                        pt = list(zip(*line.xy))[0]
                        face = key
                        offset = hvent_poly.distance(Point(pt))
                        self.s.query(
                            "UPDATE aamks_geom SET face=?, face_offset=? WHERE global_type_id=? AND type_pri='HVENT'",
                            (face, offset, i['vent_id']))
# }}}

    def _hvents_per_room(self):  # {{{
        '''
        If there are more than one vent in a room Cfast needs them enumerated.
        '''

        i = 0
        j = 0
        update = []
        for v in self.s.query(
                "SELECT name,vent_from,vent_to FROM aamks_geom WHERE type_pri='HVENT' ORDER BY vent_from,vent_to"
        ):
            if v['vent_from'] != i:
                i = v['vent_from']
                j = 0
            j += 1
            update.append((j, v['name']))
        self.s.executemany(
            'UPDATE aamks_geom SET hvent_room_seq=? WHERE name=?', (update))
# }}}

    def _vvents_per_room(self):  # {{{
        '''
        If there are more than one vent in a room Cfast needs them enumerated.
        '''
        i = 0
        j = 0
        update = []
        for v in self.s.query(
                "SELECT name,vent_from,vent_to FROM aamks_geom WHERE type_pri='VVENT' ORDER BY vent_from,vent_to"
        ):
            if v['vent_from'] != i:
                i = v['vent_from']
                j = 0
            j += 1
            update.append((j, v['name']))
        self.s.executemany(
            'UPDATE aamks_geom SET vvent_room_seq=? WHERE name=?', (update))
# }}}

    def _find_intersections_within_floor(self):  # {{{
        ''' 
        Find intersections (hvents vs compas). This is how we know which doors
        belong to which compas. We expect that HVENT intersects either:

            a) room_from, room_to  (two rectangles)
            b) room_from, outside  (one rectangle)

        If the door originates at the very beginning of the room, then it also
        has a tiny intersection with some third rectangle which we filter out
        with:

            intersection.length > 100 (intersection perimeter, 100 is arbitrary)

        self.aamks_polies is a dict of floors:
            COMPA: OrderedDict([(1, OrderedDict([(42, <shapely.geometry.polygon.Polygon object at 0x2b2206282e48>), (43, <shapely.geometry.polygon.Polygon object at 0x2b2206282eb8>), (44, <shapely.geometry.polygon.Polygon object at 0x2b2206282f28>)))]) ...
            HVENT: OrderedDict([(1, OrderedDict([(21, <shapely.geometry.polygon.Polygon object at 0x2b2206282fd0>), (22, <shapely.geometry.polygon.Polygon object at 0x2b2206293048>)))]) ...
            VVENT: OrderedDict([(1, OrderedDict([(1, <shapely.geometry.polygon.Polygon object at 0x2b2206298550>)]))])

        We aim at having vc_intersections (Vent_x_Compa intersections) dict of vents. Each vent collects two compas: 
            1: [48, 29]
            2: [49, 29]
            3: [11 ]    -> [11, 99(Outside)] 
            
        v=sorted(v) asserts that we go from lower_vent_id to higher_vent_id

        Also, we need to make sure room A and room B do intersect if there is door from A to B.
        '''

        update = []
        for floor, vents_dict in self.aamks_polies['HVENT'].items():
            all_hvents = [
                z['global_type_id'] for z in self.s.query(
                    "SELECT global_type_id FROM aamks_geom WHERE type_pri='HVENT' AND floor=? ORDER BY name",
                    floor)
            ]
            vc_intersections = {key: [] for key in all_hvents}
            for vent_id, vent_poly in vents_dict.items():
                for compa_id, compa_poly in self.aamks_polies['COMPA'][
                        floor].items():
                    if vent_poly.intersection(compa_poly).length > 100:
                        vc_intersections[vent_id].append(compa_id)

            for vent_id, v in vc_intersections.items():
                v = sorted(v)
                if len(v) == 2 and self.aamks_polies['COMPA'][floor][
                        v[0]].intersects(
                            self.aamks_polies['COMPA'][floor][v[1]]) == False:
                    self.make_vis("Space between compas", vent_id)
                if len(v) == 1:
                    v.append(self.outside_compa)
                if len(v) > 2:
                    self.make_vis(
                        'Door intersects no rooms or more than 2 rooms.',
                        vent_id)
                update.append((v[0], v[1], vent_id))
        self.s.executemany(
            "UPDATE aamks_geom SET vent_from=?, vent_to=? where global_type_id=? and type_pri='HVENT'",
            update)

# }}}

    def _find_intersections_between_floors(self):  # {{{
        '''
        Similar to _find_intersections_within_floor() This time we are looking for a
        vvent (at floor n) which intersects a compa at it's floor (floor n)
        and a compa above (floor n+1) We will iterate over two_floors, which
        can contain: a) the set of compas from (floor n) and (floor n+1) b)
        the set of compas from (floor n) only if it is the top most floor --
        outside_compa will come into play

        As opposed to _find_intersections_between_floors() vents go from higher to lower:
            "UPDATE aamks_geom SET vent_to=?, vent_from=?"

            intersection.length > 100 (intersection perimeter, 100 is arbitrary)
        '''

        update = []
        for floor, vents_dict in self.aamks_polies['VVENT'].items():
            all_vvents = [
                z['global_type_id'] for z in self.s.query(
                    "SELECT global_type_id FROM aamks_geom WHERE type_pri='VVENT' AND floor=? ORDER BY name",
                    floor)
            ]
            vc_intersections = {key: [] for key in all_vvents}
            for vent_id, vent_poly in vents_dict.items():
                try:
                    two_floors = self.aamks_polies['COMPA'][
                        floor] + self.aamks_polies['COMPA'][floor + 1]
                except:
                    two_floors = self.aamks_polies['COMPA'][floor]
                for compa_id, compa_poly in two_floors.items():
                    if vent_poly.intersection(compa_poly).length > 100:
                        vc_intersections[vent_id].append(compa_id)

            for vent_id, v in vc_intersections.items():
                v = sorted(v)
                if len(v) == 1:
                    v.append(self.outside_compa)
                if len(v) > 2:
                    self.make_vis(
                        'Vent intersects no rooms or more than 2 rooms.',
                        vent_id)
                update.append((v[0], v[1], vent_id))
        self.s.executemany(
            "UPDATE aamks_geom SET vent_to=?, vent_from=? where global_type_id=? and type_pri='VVENT'",
            update)

# }}}

    def _towers_slices(self):  # {{{
        ''' 
        This is for evacuation only and cannot interfere with fire models
        (fire_model_ignore=1). Most STAI(RCASES) or HALL(S) are drawn on floor
        0, but they are tall and need to cross other floors (towers). Say we
        have floor bottoms at 0, 3, 6, 9, 12 metres and STAI's top is at 9
        metres - the STAI belongs to floors 0, 1, 2. We INSERT (x,y) STAI
        slices on proper floors. The slices are enumerated from 100000. 
        
        '''

        towers = {}

        for w in self.s.query(
                "SELECT name,z0 as tower_z0,height+z0 as tower_z1,floor,height,type_sec FROM aamks_geom WHERE type_sec in ('STAI','HALL')"
        ):
            floor_max_z = self.s.query(
                "SELECT max(z1) FROM aamks_geom WHERE type_sec NOT IN('STAI','HALL') AND floor=?",
                (w['floor'], ))[0]['max(z1)']
            if w['tower_z1'] >= floor_max_z + 200:

                towers[w['name']] = []
                current_floor = w['floor']

                for floor in self.floors:
                    for v in self.s.query(
                            "SELECT min(z0) FROM aamks_geom WHERE type_pri='COMPA' AND floor=?",
                        (floor, )):
                        if v['min(z0)'] < w['tower_z1'] and v['min(z0)'] >= w[
                                'tower_z0']:
                            towers[w['name']].append(floor)
                towers[w['name']].remove(current_floor)

        self.towers_parents = {}
        high_global_type_id = 1000001
        for orig_name, floors in towers.items():
            orig_record = self.s.query(
                "SELECT global_type_id,type_pri,type_sec,type_tri,x0,y0,width,depth,x1,y1,room_area,room_enter,points,1 as fire_model_ignore, terminal_door FROM aamks_geom WHERE name=?",
                (orig_name, ))[0]
            parent_id = orig_record['global_type_id']
            kk = list(orig_record.keys())
            kk.append('floor')
            kk.append('name')
            for flo in floors:
                self.towers_parents[high_global_type_id] = parent_id
                orig_record['global_type_id'] = high_global_type_id
                vv = list(orig_record.values())
                vv.append(flo)
                vv.append("{}.{}".format(orig_name, flo))
                self.s.query(
                    "INSERT INTO aamks_geom ({}) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
                    .format(",".join(kk)), tuple(vv))
                high_global_type_id += 1

# }}}

# ASSERTIONS

    def _assert_faces_ok(self):  # {{{
        ''' Are all hvents' faces fine? '''
        for v in self.s.query(
                "SELECT * FROM aamks_geom WHERE type_tri='DOOR' ORDER BY vent_from,vent_to"
        ):
            assert v['face_offset'] is not None, self.make_vis(
                'Problem with cfast face calculation.', v['global_type_id'])
# }}}

    def _assert_room_has_door(self):  # {{{
        # TODO: MUST ENABLE!
        return
        ''' Each room must have at least one type_tri DOOR. '''
        doors_intersect_room_ids = []
        for i in self.s.query(
                "SELECT vent_from,vent_to FROM aamks_geom WHERE type_tri='DOOR'"
        ):
            doors_intersect_room_ids.append(i['vent_from'])
            doors_intersect_room_ids.append(i['vent_to'])

        all_interected_room = set(doors_intersect_room_ids)
        for i in self.s.query(
                "SELECT name,floor,global_type_id FROM aamks_geom WHERE type_pri='COMPA'"
        ):
            if i['global_type_id'] not in all_interected_room:
                self.make_vis('Room without door (see Animator)',
                              i['global_type_id'], 'COMPA')
# }}}

    def make_vis(self, title, faulty_id='', type_pri='HVENT'):  # {{{
        ''' 
        This method is for visualizing both errors and just how things look. 
        If faulty_id comes non-empty then we are signaling an error.
        '''

        if faulty_id != '':
            r = self.s.query(
                "SELECT name,floor FROM aamks_geom WHERE type_pri=? AND global_type_id=?",
                (type_pri, faulty_id))[0]
            fatal = "Fatal: {}: {}".format(r['name'], title)
            Vis({
                'highlight_geom': r['name'],
                'anim': None,
                'title': "<div id=python_msg>{}</div>".format(fatal),
                'srv': 1
            })
            print(fatal)
            sys.exit()
        else:
            Vis({
                'highlight_geom': None,
                'anim': None,
                'title': title,
                'srv': 1
            })
# }}}

    def _debug(self):  # {{{
        #dd(os.environ['AAMKS_PROJECT'])
        #self.s.dumpall()
        #self.s.dump_geoms()
        #dd(self.s.query("select * from aamks_geom"))
        #dd(self.s.query("select * from world2d"))
        #exit()
        #self.s.dump()
        pass
예제 #24
0
class EvacMcarlo():
    def __init__(self):  # {{{
        ''' Generate montecarlo evac.conf. '''

        self.s = Sqlite("{}/aamks.sqlite".format(os.environ['AAMKS_PROJECT']))
        self.json = Json()
        self.conf = self.json.read("{}/conf.json".format(
            os.environ['AAMKS_PROJECT']))
        self.floors = [
            z['floor'] for z in self.s.query(
                "SELECT DISTINCT floor FROM aamks_geom ORDER BY floor")
        ]
        self._project_name = os.path.basename(os.environ['AAMKS_PROJECT'])

        si = SimIterations(self.conf['project_id'],
                           self.conf['number_of_simulations'])
        for self._sim_id in range(*si.get()):
            seed(self._sim_id)
            self._static_evac_conf()
            self._dispatch_evacuees()
            self._make_evac_conf()

# }}}

    def _static_evac_conf(self):  # {{{
        ''' 
        AAMKS_PROJECT must be propagated to worker environment.
        '''

        fire_origin_file = "{}/workers/{}/fire_origin.json".format(
            os.environ['AAMKS_PROJECT'], self._sim_id)

        self._evac_conf = self.conf
        self._evac_conf['AAMKS_PROJECT'] = os.environ['AAMKS_PROJECT']
        self._evac_conf['SIM_ID'] = self._sim_id
        self._evac_conf['SERVER'] = os.environ['AAMKS_SERVER']
        self._evac_conf['FIRE_ORIGIN'] = self.json.read(fire_origin_file)
        os.remove(fire_origin_file)
# }}}

    def _make_pre_evacuation(self, room, type_sec):  # {{{
        ''' 
        An evacuee pre_evacuates from either ordinary room or from the room of
        fire origin; type_sec is for future development.
        '''

        if room != self._evac_conf['FIRE_ORIGIN']:
            pe = self.conf['pre_evac_fire_origin']
        else:
            pe = self.conf['pre_evac']
        return round(lognorm(s=1, loc=pe['mean'], scale=pe['sd']).rvs(), 2)
# }}}

    def _get_density(self, name, type_sec, floor):  # {{{
        ''' Special selectors from distributions.json
        First we try to return ROOM_1_2, then ROOM_FLOOR_1, then ROOM
        '''

        z = self.conf['evacuees_concentration']
        for i in [name, "{}_FLOOR_{}".format(type_sec, floor), type_sec]:
            if i in z.keys():
                return z[i]
        raise Exception("Cannot determine the density for {}".format(name))

# }}}

    def _dispatch_evacuees(self):  # {{{
        ''' 
        We dispatch the evacuees across the building according to the density
        distribution. 
        '''

        self.dispatched_evacuees = OrderedDict()
        self.pre_evacuation = OrderedDict()
        for floor in self.floors:
            self.pre_evacuation[floor] = list()
            positions = []
            rooms = self.s.query(
                "SELECT x0, x1, y0, y1, name, type_sec, room_area FROM aamks_geom WHERE type_pri='COMPA' AND floor=?",
                (floor, ))
            for r in rooms:
                density = self._get_density(r['name'], r['type_sec'], floor)
                x = uniform(r['x0'] + 50, r['x1'] - 50,
                            int(r['room_area'] / density))
                y = uniform(r['y0'] + 50, r['y1'] - 50,
                            int(r['room_area'] / density))
                positions += list(zip(x, y))
                for i in x:
                    self.pre_evacuation[floor].append(
                        self._make_pre_evacuation(r['name'], r['type_sec']))
            self.dispatched_evacuees[floor] = [
                list([int(i) for i in l]) for l in positions
            ]
# }}}

    def _make_evac_conf(self):  # {{{
        ''' Write data to sim_id/evac.json. '''

        self._evac_conf['FLOORS_DATA'] = OrderedDict()
        for floor in self.floors:
            self._evac_conf['FLOORS_DATA'][floor] = OrderedDict()
            self._evac_conf['FLOORS_DATA'][floor]['NUM_OF_EVACUEES'] = len(
                self.dispatched_evacuees[floor])
            self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'] = OrderedDict()
            z = self.s.query("SELECT z0 FROM aamks_geom WHERE floor=?",
                             (floor, ))[0]['z0']
            for i, pos in enumerate(self.dispatched_evacuees[floor]):
                e_id = 'E{}'.format(i)
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][
                    e_id] = OrderedDict()
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id][
                    'ORIGIN'] = (pos[0], pos[1])
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id][
                    'PRE_EVACUATION'] = self.pre_evacuation[floor][i]

                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id][
                    'ALPHA_V'] = round(
                        normal(self.conf['evacuees_alpha_v']['mean'],
                               self.conf['evacuees_alpha_v']['sd']), 2)
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id][
                    'BETA_V'] = round(
                        normal(self.conf['evacuees_beta_v']['mean'],
                               self.conf['evacuees_beta_v']['sd']), 2)
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id][
                    'H_SPEED'] = round(
                        normal(self.conf['evacuees_max_h_speed']['mean'],
                               self.conf['evacuees_max_h_speed']['sd']), 2)
                self._evac_conf['FLOORS_DATA'][floor]['EVACUEES'][e_id][
                    'V_SPEED'] = round(
                        normal(self.conf['evacuees_max_v_speed']['mean'],
                               self.conf['evacuees_max_v_speed']['sd']), 2)

        self.json.write(
            self._evac_conf,
            "{}/workers/{}/evac.json".format(os.environ['AAMKS_PROJECT'],
                                             self._sim_id))