Example #1
0
    def run(self):
        dataset_pool = SessionConfiguration().get_dataset_pool()
        current_year = SimulationState().get_current_time()
        building_set = dataset_pool.get_dataset('building')
        parcel_set = dataset_pool.get_dataset('parcel')
        proposal_set = dataset_pool.get_dataset('proposed_development_event')
        parcels = proposal_set.get_attribute('parcel_id')
        amount = proposal_set.get_attribute('amount')
        year = proposal_set.get_attribute('year')
        proposal_in_current_year = (year==current_year)
        building_type_id = proposal_set.get_attribute('building_type_id')
        proposed_sf_units = amount * (building_type_id == 1)*proposal_in_current_year
        proposed_townhome_units = amount * (building_type_id == 2)*proposal_in_current_year
        proposed_mf_units = amount * (building_type_id == 3)*proposal_in_current_year
        proposal_zone = proposal_in_current_year * proposal_set.compute_variables('_proposal_zone = proposed_development_event.disaggregate(parcel.zone_id)')
        parcel_zone = parcel_set.get_attribute('zone_id')
        zone_ids = unique(parcel_zone)
        in_zone = None
        for zone_id in zone_ids:
            # proposals_in_zone = where(proposal_zone==zone_id)[0]
            # logger.log_status("proposals_in_zone %s: %s" % (zone_id,proposals_in_zone.size))
            # if proposals_in_zone.size > 0:
                # logger.log_status("sf units in zone %s: %s" % (zone_id,proposed_sf_units[proposals_in_zone].sum()))
                # logger.log_status("townhome units in zone %s: %s" % (zone_id,proposed_townhome_units[proposals_in_zone].sum()))
                # logger.log_status("mf units in zone %s: %s" % (zone_id,proposed_mf_units[proposals_in_zone].sum()))
                ######Need to come back and look at the treatment of proposed projects.  But this won't affect calibration/validation, so proceed for now.
            if in_zone is not None:
                building_set.delete_computed_attributes()
                parcel_set.delete_computed_attributes()
            in_zone = building_set.compute_variables('_in_zone = building.disaggregate(parcel.zone_id)==%s'% (zone_id))
            idx_in_zone = where(in_zone)[0]
            max_building_id = (building_set.get_attribute('building_id')).max()
            attribs = building_set.get_known_attribute_names()
            table_data={}
            for name in attribs:
                table_data["%s"%(name)]=building_set.get_attribute("%s" %(name))[idx_in_zone]
            storage = StorageFactory().get_storage('dict_storage')
            table_name = 'buildings_zone'
            storage.write_table(
                table_name=table_name,
                table_data=table_data
                )
            buildings_zone = BuildingDataset(in_storage=storage, in_table_name=table_name)
            if buildings_zone.size() > 0:
                dptm = RealEstateTransitionModel(target_vancy_dataset=dataset_pool.get_dataset('target_vacancy'))
                results, index = dptm.run(realestate_dataset = buildings_zone,
                                   year = current_year,
                                   occupied_spaces_variable = 'occupied_spaces',
                                   total_spaces_variable = 'total_spaces',
                                   target_attribute_name = 'target_vacancy_rate',
                                   sample_from_dataset = dataset_pool.get_dataset('building'),
                                   dataset_pool=dataset_pool,
                                   append_to_realestate_dataset = False,
                                   reset_attribute_value={'parcel_id':-1},
                                   sample_filter="(building.building_type_id==1)*(building.year_built>1989) +  (building.building_type_id==3)*(building.year_built>1979) +  (building.building_type_id==2)*(building.year_built>1989)+ (building.building_type_id>3)*(building.building_type_id<12)"
                                   )
                #This is where, now that we know the demand for sqft, I'd want to insert the appropriate amount of permitted/proposed projects.
                if results is not None:
                    results.modify_attribute('year_built', array(index.size*[current_year]))
                    attribs2 = results.get_known_attribute_names()
                    table_data2={}
                    for name in attribs2:
                        if name in attribs:
                            table_data2["%s"%(name)]=results.get_attribute("%s" %(name))
                    building_set.add_elements(data=table_data2, require_all_attributes=False, change_ids_if_not_unique=True)

                    index_new_sf_units = where(logical_and(building_set['building_id']>max_building_id, building_set['building_type_id']==1))[0]
                    index_new_mf_units = where(logical_and(building_set['building_id']>max_building_id, (building_set['building_type_id']==2)+(building_set['building_type_id']==3)))[0]
                    index_new_industrial_units = where(logical_and(building_set['building_id']>max_building_id, (building_set['building_type_id']==6)+(building_set['building_type_id']==7)))[0]
                    index_new_commercial_units = where(logical_and(building_set['building_id']>max_building_id, (building_set['building_type_id']==9)+(building_set['building_type_id']==8)))[0]
                    if index_new_sf_units.size > 0:
                        for building in index_new_sf_units:
                            has_available_sf_capacity = parcel_set.compute_variables('_has_available_sf_capacity = (parcel.zone_id==%s) * parcel.disaggregate(zoning.allow_sf==1) * (((safe_array_divide(parcel.parcel_sqft,parcel.disaggregate(zoning.min_lot_size)).round().astype(int32)) - (parcel.number_of_agents(building)))>0)'%(zone_id))
                            idx_has_available_sf_capacity = where(has_available_sf_capacity)[0]
                            if idx_has_available_sf_capacity.size < 1:
                                logger.log_status("No more single-family capacity remaining in this zone")
                                break
                            parcel_ids_available_sf_capacity=(parcel_set.get_attribute('parcel_id'))[idx_has_available_sf_capacity]
                            shuffle(parcel_ids_available_sf_capacity)
                            parcel_id_to_assign = parcel_ids_available_sf_capacity[:1]
                            building_set.modify_attribute('parcel_id', parcel_id_to_assign, building)
                    if index_new_commercial_units.size > 0:
                        for building in index_new_commercial_units:
                            available_commercial_capacity = parcel_set.compute_variables('_available_commercial_capacity = (parcel.zone_id==%s) * parcel.disaggregate(zoning.allow_comm==1) * clip_to_zero((((parcel.parcel_sqft)*parcel.disaggregate(zoning.max_far)).round().astype(int32)-(parcel.aggregate(1000*building.residential_units))) - (parcel.aggregate(building.non_residential_sqft)))'%(zone_id))
                            ####update the capacity calcs to account for sqft per unit of hotel/resort units
                            building_sqft = building_set['non_residential_sqft'][building]
                            idx_building_would_fit = where(available_commercial_capacity>=building_sqft)[0]
                            if idx_building_would_fit.size < 1:
                                logger.log_status("No more commercial capacity remaining in this zone")
                                break
                            parcel_ids_with_enough_capacity = (parcel_set.get_attribute('parcel_id'))[idx_building_would_fit]
                            shuffle(parcel_ids_with_enough_capacity) #replace with code involving random/uniform/cumprob/searchsorted etc...  I think it would be faster
                            parcel_id_to_assign = parcel_ids_with_enough_capacity[:1]
                            building_set.modify_attribute('parcel_id', parcel_id_to_assign, building)
                    if index_new_industrial_units.size > 0:
                        for building in index_new_industrial_units:
                            available_industrial_capacity = parcel_set.compute_variables('_available_industrial_capacity = (parcel.zone_id==%s) * parcel.disaggregate(zoning.allow_indust==1) * clip_to_zero((((parcel.parcel_sqft)*parcel.disaggregate(zoning.max_far)).round().astype(int32)-(parcel.aggregate(1000*building.residential_units))) - (parcel.aggregate(building.non_residential_sqft)))'%(zone_id))
                            building_sqft = building_set['non_residential_sqft'][building]
                            idx_building_would_fit = where(available_industrial_capacity>=building_sqft)[0]
                            if idx_building_would_fit.size < 1:
                                logger.log_status("No more industrial capacity remaining in this zone")
                                break
                            parcel_ids_with_enough_capacity = (parcel_set.get_attribute('parcel_id'))[idx_building_would_fit]
                            shuffle(parcel_ids_with_enough_capacity) 
                            parcel_id_to_assign = parcel_ids_with_enough_capacity[:1]
                            building_set.modify_attribute('parcel_id', parcel_id_to_assign, building)
                    if index_new_mf_units.size > 0:
                        for building in index_new_mf_units:
                            available_mf_capacity = parcel_set.compute_variables('_available_mf_capacity = (parcel.zone_id==%s) * parcel.disaggregate(zoning.allow_mf==1) * clip_to_zero(((((parcel.parcel_sqft)*parcel.disaggregate(zoning.max_far)).round().astype(int32)) - (parcel.aggregate(building.non_residential_sqft)))/1000 - (parcel.aggregate(building.residential_units)))'%(zone_id))
                            building_resunits = building_set['residential_units'][building]
                            idx_building_would_fit = where(available_mf_capacity>=building_resunits)[0]
                            if idx_building_would_fit.size < 1:
                                logger.log_status("No more multifamily capacity remaining in this zone")
                                break
                            parcel_ids_with_enough_capacity = (parcel_set.get_attribute('parcel_id'))[idx_building_would_fit] 
                            shuffle(parcel_ids_with_enough_capacity)
                            parcel_id_to_assign = parcel_ids_with_enough_capacity[:1]
                            building_set.modify_attribute('parcel_id', parcel_id_to_assign, building)
                    index_unplaced_buildings = where(logical_and(building_set['building_id']>max_building_id, building_set['parcel_id']<=0))[0]
                    logger.log_status("Number of unplaced buildings to be removed from zone%s: %s" % (zone_id,index_unplaced_buildings.size))
                    building_set.remove_elements(index_unplaced_buildings)
Example #2
0
    def run(self):

        dataset_pool = SessionConfiguration().get_dataset_pool()
        submarket_set = dataset_pool.get_dataset('submarket')
        building_set = dataset_pool.get_dataset('building')
        parcel_set = dataset_pool.get_dataset('parcel')
        current_year = SimulationState().get_current_time()
        dptm = RealEstateTransitionModel(target_vancy_dataset=dataset_pool.get_dataset('target_vacancy'))
        results, index = dptm.run(realestate_dataset = dataset_pool.get_dataset('building'),
                           year = current_year,
                           occupied_spaces_variable = 'occupied_spaces',
                           total_spaces_variable = 'total_spaces',
                           target_attribute_name = 'target_vacancy_rate',
                           sample_from_dataset = dataset_pool.get_dataset('building'),
                           dataset_pool=dataset_pool,
                           append_to_realestate_dataset = True,
                           reset_attribute_value={'parcel_id':-1},
                           sample_filter="(building.building_type_id==1)*(building.year_built>1989) +  (building.building_type_id==3)*(building.year_built>1979) +  (building.building_type_id==2)*(building.year_built>1989)+ (building.building_type_id>3)*(building.building_type_id<12)"
                           #year_built = 2001,
                           #table_name = 'buildings',
                           #dataset_name = 'building',
                           #id_name = 'building_id'
                           )
                           #resources=self.compute_resources)
        logger.log_status("results: %s" % (results))
        logger.log_status("index: %s" % (index))
        logger.log_status("results_size: %s" % (results.size()))
        logger.log_status("index_size: %s" % (index.size))
        number_of_new_residential_units = results.get_attribute('residential_units')[index].sum()
        logger.log_status("number_of_new_residential_units: %s" % (number_of_new_residential_units))
        new_nonres_sqft = (results.get_attribute('non_residential_sqft') * (results.get_attribute('building_type_id')>3))[index].sum()
        logger.log_status("new_nonres_sqft: %s" % (new_nonres_sqft))

        logger.log_status("building_set: %s" % (building_set))
        logger.log_status("building_set_size: %s" % (building_set.size()))
        number_of_new_residential_units = building_set.get_attribute('residential_units')[index].sum()
        logger.log_status("number_of_new_residential_units: %s" % (number_of_new_residential_units))
        new_nonres_sqft = (building_set.get_attribute('non_residential_sqft') * (building_set.get_attribute('building_type_id')>3))[index].sum()
        logger.log_status("new_nonres_sqft: %s" % (new_nonres_sqft))

        new_sf_units = (building_set.get_attribute('residential_units') * (building_set.get_attribute('building_type_id')==1))[index].sum()
        new_townhome_units = (building_set.get_attribute('residential_units') * (building_set.get_attribute('building_type_id')==2))[index].sum()
        new_apartment_units = (building_set.get_attribute('residential_units') * (building_set.get_attribute('building_type_id')==3))[index].sum()
        new_hotel_sqft = (building_set.get_attribute('non_residential_sqft') * (building_set.get_attribute('building_type_id')==4))[index].sum()
        new_resort_sqft = (building_set.get_attribute('non_residential_sqft') * (building_set.get_attribute('building_type_id')==5))[index].sum()
        new_industrial_sqft = (building_set.get_attribute('non_residential_sqft') * (building_set.get_attribute('building_type_id')==6))[index].sum()
        new_warehouse_sqft = (building_set.get_attribute('non_residential_sqft') * (building_set.get_attribute('building_type_id')==7))[index].sum()
        new_office_sqft = (building_set.get_attribute('non_residential_sqft') * (building_set.get_attribute('building_type_id')==8))[index].sum()
        new_retail_sqft = (building_set.get_attribute('non_residential_sqft') * (building_set.get_attribute('building_type_id')==9))[index].sum()
        new_community_sqft = (building_set.get_attribute('non_residential_sqft') * (building_set.get_attribute('building_type_id')==10))[index].sum()
        new_other_sqft = (building_set.get_attribute('non_residential_sqft') * (building_set.get_attribute('building_type_id')==11))[index].sum()
        logger.log_status("new_sf_units: %s" % (new_sf_units))
        logger.log_status("new_townhome_units: %s" % (new_townhome_units))
        logger.log_status("new_apartment_units: %s" % (new_apartment_units))
        logger.log_status("new_hotel_sqft: %s" % (new_hotel_sqft))
        logger.log_status("new_resort_sqft: %s" % (new_resort_sqft))
        logger.log_status("new_industrial_sqft: %s" % (new_industrial_sqft))
        logger.log_status("new_warehouse_sqft: %s" % (new_warehouse_sqft))
        logger.log_status("new_office_sqft: %s" % (new_office_sqft))
        logger.log_status("new_retail_sqft: %s" % (new_retail_sqft))
        logger.log_status("new_community_sqft: %s" % (new_community_sqft))
        logger.log_status("new_other_sqft: %s" % (new_other_sqft))
        #This is where, now that we know the demand for sqft, I'd want to insert the appropriate amount of permitted/proposed projects.
        building_set.modify_attribute('year_built', array(index.size*[current_year]), index)
        minimum_new_building_id = (building_set.get_attribute('building_id'))[index].min()
        max_old_building_id = minimum_new_building_id - 1
        logger.log_status("minimum_new_building_id: %s" % (minimum_new_building_id))
        index_new_sf_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==1))[0]
        logger.log_status("index_new_sf_units_size: %s" % (index_new_sf_units.size))
        logger.log_status("index_new_sf_units number resunits: %s" % ((building_set['residential_units'])[index_new_sf_units].sum()))
        #Ok, so come up with a random list of eligible parcel_id's of length index_new_sf_units, then assign these parcel_id's to this index of buildings
        has_available_sf_capacity = parcel_set.compute_variables('_has_available_sf_capacity = parcel.disaggregate(zoning.allow_sf==1) * (((safe_array_divide(parcel.parcel_sqft,parcel.disaggregate(zoning.min_lot_size)).round().astype(int32)) - (parcel.number_of_agents(building)))>0)')
        idx_has_available_sf_capacity = where(has_available_sf_capacity)[0]
        parcel_ids_available_sf_capacity=(parcel_set.get_attribute('parcel_id'))[idx_has_available_sf_capacity]
        logger.log_status("idx_has_available_sf_capacity: %s" % (idx_has_available_sf_capacity))
        logger.log_status("idx_has_available_sf_capacity_size: %s" % (idx_has_available_sf_capacity.size))
        logger.log_status("parcel_ids_available_sf_capacity: %s" % (parcel_ids_available_sf_capacity))
        logger.log_status("parcel_ids_available_sf_capacity_size: %s" % (parcel_ids_available_sf_capacity.size))
        shuffle(parcel_ids_available_sf_capacity)
        parcel_ids_to_assign = parcel_ids_available_sf_capacity[:index_new_sf_units.size]
        logger.log_status("parcel_ids_to_assign: %s" % (parcel_ids_to_assign))
        logger.log_status("parcel_ids_to_assign_size: %s" % (parcel_ids_to_assign.size))
        building_set.modify_attribute('parcel_id', parcel_ids_to_assign, index_new_sf_units)
        #remember at the end of this program to delete all buildings with parcel_id of -1.  These buildings should not be placed because zoning is maxed out (e.g. sf detached will prolly be maxed after 10 yr...)
        #index_new_townhome_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==2))[0]
        #index_new_apartment_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==3))[0]
        index_new_mf_units = where(logical_and(building_set['building_id']>max_old_building_id, (building_set['building_type_id']==2)+(building_set['building_type_id']==3)))[0]
        index_new_hotel_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==4))[0]
        index_new_resort_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==5))[0]
        #index_new_industrial_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==6))[0]
        #index_new_warehouse_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==7))[0]
        index_new_industrial_units = where(logical_and(building_set['building_id']>max_old_building_id, (building_set['building_type_id']==6)+(building_set['building_type_id']==7)))[0]
        #index_new_office_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==8))[0]
        #index_new_retail_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==9))[0]
        index_new_commercial_units = where(logical_and(building_set['building_id']>max_old_building_id, (building_set['building_type_id']==9)+(building_set['building_type_id']==8)))[0]
        index_new_community_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==10))[0]
        index_new_other_units = where(logical_and(building_set['building_id']>max_old_building_id, building_set['building_type_id']==11))[0]
        #logger.log_status("index_new_townhome_units_size: %s" % (index_new_townhome_units.size))
        #logger.log_status("index_new_apartment_units: %s" % (index_new_apartment_units.size))
        logger.log_status("index_new_mf_units: %s" % (index_new_mf_units.size))
        logger.log_status("index_new_hotel_units: %s" % (index_new_hotel_units.size))
        logger.log_status("index_new_resort_units: %s" % (index_new_resort_units.size))
        #logger.log_status("index_new_warehouse_units: %s" % (index_new_warehouse_units.size))
        logger.log_status("index_new_industrial_units: %s" % (index_new_industrial_units.size))
        #logger.log_status("index_new_office_units: %s" % (index_new_office_units.size))
        #logger.log_status("index_new_retail_units: %s" % (index_new_retail_units.size))
        logger.log_status("index_new_commercial_units: %s" % (index_new_commercial_units.size))
        logger.log_status("index_new_community_units: %s" % (index_new_community_units.size))
        logger.log_status("index_new_other_units: %s" % (index_new_other_units.size))
        #logger.log_status("index_new_townhome_units number resunits: %s" % ((building_set['residential_units'])[index_new_townhome_units].sum()))
        #logger.log_status("index_new_apartment_units number resunits: %s" % ((building_set['residential_units'])[index_new_apartment_units].sum()))
        logger.log_status("index_new_mf_units number resunits: %s" % ((building_set['residential_units'])[index_new_mf_units].sum()))
        logger.log_status("index_new_hotel_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_hotel_units].sum()))
        logger.log_status("index_new_resort_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_resort_units].sum()))
        #logger.log_status("index_new_industrial_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_industrial_units].sum()))
        #logger.log_status("index_new_warehouse_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_warehouse_units].sum()))
        logger.log_status("index_new_industrial_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_industrial_units].sum()))
        #logger.log_status("index_new_office_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_office_units].sum()))
        #logger.log_status("index_new_retail_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_retail_units].sum()))
        logger.log_status("index_new_commercial_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_commercial_units].sum()))
        logger.log_status("index_new_community_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_community_units].sum()))
        logger.log_status("index_new_other_units nonres_sqft: %s" % ((building_set['non_residential_sqft'])[index_new_other_units].sum()))
        #Ok, so come up with a random list of eligible parcel_id's of length index_new_sf_units, then assign these parcel_id's to this index of buildings
        #available_commercial_capacity = parcel_set.compute_variables('_available_commercial_capacity = parcel.disaggregate(zoning.allow_comm==1) * clip_to_zero((((parcel.parcel_sqft)*parcel.disaggregate(zoning.max_far)).round().astype(int32)-(parcel.aggregate(1000*building.residential_units))) - (parcel.aggregate(building.non_residential_sqft)))')
        for building in index_new_commercial_units:
            available_commercial_capacity = parcel_set.compute_variables('_available_commercial_capacity = parcel.disaggregate(zoning.allow_comm==1) * clip_to_zero((((parcel.parcel_sqft)*parcel.disaggregate(zoning.max_far)).round().astype(int32)-(parcel.aggregate(1000*building.residential_units))) - (parcel.aggregate(building.non_residential_sqft)))')
            logger.log_status("available_commercial_capacity: %s" % (available_commercial_capacity))
            building_sqft = building_set['non_residential_sqft'][building]
            logger.log_status("building_sqft: %s" % (building_sqft))
            idx_building_would_fit = where(available_commercial_capacity>=building_sqft)[0]
            logger.log_status("idx_building_would_fit: %s" % (idx_building_would_fit))
            parcel_ids_with_enough_capacity = (parcel_set.get_attribute('parcel_id'))[idx_building_would_fit]
            logger.log_status("parcel_ids_with_enough_capacity: %s" % (parcel_ids_with_enough_capacity))
            shuffle(parcel_ids_with_enough_capacity) #replace with code involving random/uniform/cumprob/searchsorted etc...  I think it would be faster
            logger.log_status("parcel_ids_with_enough_capacity: %s" % (parcel_ids_with_enough_capacity))
            parcel_id_to_assign = parcel_ids_with_enough_capacity[:1]
            logger.log_status("parcel_id_to_assign: %s" % (parcel_id_to_assign))
            building_set.modify_attribute('parcel_id', parcel_id_to_assign, building)
        for building in index_new_industrial_units:
            available_industrial_capacity = parcel_set.compute_variables('_available_industrial_capacity = parcel.disaggregate(zoning.allow_indust==1) * clip_to_zero((((parcel.parcel_sqft)*parcel.disaggregate(zoning.max_far)).round().astype(int32)-(parcel.aggregate(1000*building.residential_units))) - (parcel.aggregate(building.non_residential_sqft)))')
            logger.log_status("available_industrial_capacity: %s" % (available_industrial_capacity))
            building_sqft = building_set['non_residential_sqft'][building]
            logger.log_status("building_sqft: %s" % (building_sqft))
            idx_building_would_fit = where(available_industrial_capacity>=building_sqft)[0]
            logger.log_status("idx_building_would_fit: %s" % (idx_building_would_fit))
            parcel_ids_with_enough_capacity = (parcel_set.get_attribute('parcel_id'))[idx_building_would_fit]
            logger.log_status("parcel_ids_with_enough_capacity: %s" % (parcel_ids_with_enough_capacity))
            shuffle(parcel_ids_with_enough_capacity) #replace with code involving random/uniform/cumprob/searchsorted etc...  I think it would be faster
            logger.log_status("parcel_ids_with_enough_capacity: %s" % (parcel_ids_with_enough_capacity))
            parcel_id_to_assign = parcel_ids_with_enough_capacity[:1]
            logger.log_status("parcel_id_to_assign: %s" % (parcel_id_to_assign))
            building_set.modify_attribute('parcel_id', parcel_id_to_assign, building)
        for building in index_new_mf_units:
            available_mf_capacity = parcel_set.compute_variables('_available_mf_capacity = parcel.disaggregate(zoning.allow_mf==1) * clip_to_zero(((((parcel.parcel_sqft)*parcel.disaggregate(zoning.max_far)).round().astype(int32)) - (parcel.aggregate(building.non_residential_sqft)))/1000 - (parcel.aggregate(building.residential_units)))')
            logger.log_status("available_mf_capacity: %s" % (available_mf_capacity))
            building_resunits = building_set['residential_units'][building]
            logger.log_status("building_resunits: %s" % (building_resunits))
            idx_building_would_fit = where(available_mf_capacity>=building_resunits)[0]
            logger.log_status("idx_building_would_fit: %s" % (idx_building_would_fit))
            parcel_ids_with_enough_capacity = (parcel_set.get_attribute('parcel_id'))[idx_building_would_fit]
            logger.log_status("parcel_ids_with_enough_capacity: %s" % (parcel_ids_with_enough_capacity))
            shuffle(parcel_ids_with_enough_capacity) #replace with code involving random/uniform/cumprob/searchsorted etc...  I think it would be faster
            logger.log_status("parcel_ids_with_enough_capacity: %s" % (parcel_ids_with_enough_capacity))
            parcel_id_to_assign = parcel_ids_with_enough_capacity[:1]
            logger.log_status("parcel_id_to_assign: %s" % (parcel_id_to_assign))
            building_set.modify_attribute('parcel_id', parcel_id_to_assign, building)