def residential_developer(feasibility, households, buildings, parcels, year, settings, summary, form_to_btype_func, add_extra_columns_func): kwargs = settings['residential_developer'] new_buildings = utils.run_developer( "residential", households, buildings, "residential_units", parcels.parcel_size, parcels.ave_sqft_per_unit, parcels.total_residential_units, feasibility, year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, **kwargs) summary.add_parcel_output(new_buildings)
def residential_developer(feasibility, households, buildings, parcels, year, settings, summary, form_to_btype_func, add_extra_columns_func): kwargs = settings['residential_developer'] new_buildings = utils.run_developer( "residential", households, buildings, "residential_units", parcels.parcel_size, parcels.ave_sqft_per_unit, parcels.total_residential_units, feasibility, year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, **kwargs) summary.add_parcel_output(new_buildings)
def non_residential_developer(feasibility, jobs, buildings, parcels, year, settings, summary, form_to_btype_func, add_extra_columns_func): kwargs = settings['non_residential_developer'] new_buildings = utils.run_developer( ["office", "retail", "industrial"], jobs, buildings, "job_spaces", parcels.parcel_size, parcels.ave_sqft_per_unit, parcels.total_job_spaces, feasibility, year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, residential=False, **kwargs) summary.add_parcel_output(new_buildings)
def non_residential_developer(feasibility, jobs, buildings, parcels, year, settings, summary, form_to_btype_func, add_extra_columns_func): kwargs = settings['non_residential_developer'] new_buildings = utils.run_developer( ["office", "retail", "industrial"], jobs, buildings, "job_spaces", parcels.parcel_size, parcels.ave_sqft_per_unit, parcels.total_job_spaces, feasibility, year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, residential=False, **kwargs) summary.add_parcel_output(new_buildings)
def run_subsidized_developer(feasibility, parcels, buildings, households, acct_settings, settings, account, year, form_to_btype_func, add_extra_columns_func, summary, create_deed_restricted=False, policy_name="Unnamed"): """ The subsidized residential developer model. Parameters ---------- feasibility : DataFrame A DataFrame that is returned from run_feasibility for a given form parcels : DataFrameWrapper The standard parcels DataFrameWrapper (mostly just for run_developer) buildings : DataFrameWrapper The standard buildings DataFrameWrapper (passed to run_developer) households : DataFrameWrapper The households DataFrameWrapper (passed to run_developer) acct_settings : Dict A dictionary of settings to parameterize the model. Needs these keys: sending_buildings_subaccount_def - maps buildings to subaccounts receiving_buildings_filter - filter for eligible buildings settings : Dict The overall settings account : Account The Account object to use for subsidization year : int The current simulation year (will be added as metadata) form_to_btype_func : function Passed through to run_developer add_extra_columns_func : function Passed through to run_developer summary : Summary Used to add parcel summary information create_deed_restricted : bool Bool for whether to create deed restricted units with the subsidies or not. The logic at the time of this writing is to keep track of partial units so that when partial units sum to greater than a unit, that unit will be deed restricted. Returns ------- Nothing Subsidized residential developer is designed to run before the normal residential developer - it will prioritize the developments we're subsidizing (although this is not strictly required - running this model after the market rate developer will just create a temporarily larger supply of units, which will probably create less market rate development in the next simulated year) the steps for subsidizing are essentially these 1 run feasibility with only_built set to false so that the feasibility of unprofitable units are recorded 2 temporarily filter to ONLY unprofitable units to check for possible subsidized units (normal developer takes care of market-rate units) 3 compute the number of units in these developments 4 divide cost by number of units in order to get the subsidy per unit 5 filter developments to parcels in "receiving zone" similar to the way we identified "sending zones" 6 iterate through subaccounts one at a time as subsidy will be limited to available funds in the subaccount (usually by jurisdiction) 7 sort ascending by subsidy per unit so that we minimize subsidy (but total subsidy is equivalent to total building cost) 8 cumsum the total subsidy in the buildings and locate the development where the subsidy is less than or equal to the amount in the account - filter to only those buildings (these will likely be built) 9 pass the results as "feasible" to run_developer - this is sort of a boundary case of developer but should run OK 10 for those developments that get built, make sure to subtract from account and keep a record (on the off chance that demand is less than the subsidized units, run through the standard code path, although it's very unlikely that there would be more subsidized housing than demand) """ # step 2 feasibility = feasibility.replace([np.inf, -np.inf], np.nan) feasibility = feasibility[feasibility.max_profit < 0] # step 3 feasibility['ave_sqft_per_unit'] = parcels.ave_sqft_per_unit feasibility['residential_units'] = \ np.floor(feasibility.residential_sqft / feasibility.ave_sqft_per_unit) # step 3B # can only add units - don't subtract units - this is an approximation # of the calculation that will be used to do this in the developer model feasibility = feasibility[ feasibility.residential_units > feasibility.total_residential_units] # step 3C # towards the end, because we're about to sort by subsidy per unit, some # large projects never get built, because it could be a 100 unit project # times a 500k subsidy per unit. thus we're going to try filtering by # the maximum subsidy for a single development here feasibility = feasibility[feasibility.max_profit > -50 * 1000000] # step 4 feasibility['subsidy_per_unit'] = \ -1 * feasibility['max_profit'] / feasibility['residential_units'] # assumption that even if the developer says this property is almost # profitable, even the administration costs are likely to cost at least # 10k / unit feasibility['subsidy_per_unit'] = feasibility.subsidy_per_unit.clip(10000) # step 5 if "receiving_buildings_filter" in acct_settings: feasibility = feasibility.\ query(acct_settings["receiving_buildings_filter"]) else: # otherwise all buildings are valid pass new_buildings_list = [] sending_bldgs = acct_settings["sending_buildings_subaccount_def"] feasibility["regional"] = 1 feasibility["subaccount"] = feasibility.eval(sending_bldgs) # step 6 for subacct, amount in account.iter_subaccounts(): print "Subaccount: ", subacct df = feasibility[feasibility.subaccount == subacct] print "Number of feasible projects in receiving zone:", len(df) if len(df) == 0: continue # step 7 df = df.sort_values(['subsidy_per_unit'], ascending=True) # df.to_csv('subsidized_units_%d_%s_%s.csv' % # (orca.get_injectable("year"), account.name, subacct)) # step 8 print "Amount in subaccount: ${:,.2f}".format(amount) num_bldgs = int((-1 * df.max_profit).cumsum().searchsorted(amount)) if num_bldgs == 0: continue # technically we only build these buildings if there's demand # print "Building {:d} subsidized buildings".format(num_bldgs) df = df.iloc[:int(num_bldgs)] df.columns = pd.MultiIndex.from_tuples([("residential", col) for col in df.columns]) # disable stdout since developer is a bit verbose for this use case sys.stdout, old_stdout = StringIO(), sys.stdout kwargs = settings['residential_developer'] # step 9 new_buildings = utils.run_developer( "residential", households, buildings, "residential_units", parcels.parcel_size, parcels.ave_sqft_per_unit, parcels.total_residential_units, orca.DataFrameWrapper("feasibility", df), year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, profit_to_prob_func=profit_to_prob_func, **kwargs) sys.stdout = old_stdout buildings = orca.get_table("buildings") if new_buildings is None: continue # keep track of partial subsidized untis so that we always get credit # for a partial unit, even if it's not built in this specific building partial_subsidized_units = 0 # step 10 for index, new_building in new_buildings.iterrows(): amt = new_building.max_profit metadata = { "description": "Developing subsidized building", "year": year, "residential_units": new_building.residential_units, "building_id": index } account.add_transaction(amt, subaccount=subacct, metadata=metadata) if create_deed_restricted: revenue_per_unit = new_building.building_revenue / \ new_building.residential_units total_subsidy = abs(new_building.max_profit) subsidized_units = total_subsidy / revenue_per_unit + \ partial_subsidized_units # right now there are inclusionary requirements already_subsidized_units = new_building.deed_restricted_units # get remainder partial_subsidized_units = subsidized_units % 1 # round off for now subsidized_units = int(subsidized_units) + \ already_subsidized_units # cap at number of residential units subsidized_units = min(subsidized_units, new_building.residential_units) buildings.local.loc[index, "deed_restricted_units"] =\ int(round(subsidized_units)) # also correct the debug output new_buildings.loc[index, "deed_restricted_units"] =\ int(round(subsidized_units)) assert np.all( buildings.local.deed_restricted_units.fillna(0) <= buildings.local.residential_units.fillna(0)) print "Amount left after subsidy: ${:,.2f}".\ format(account.total_transactions_by_subacct(subacct)) new_buildings_list.append(new_buildings) total_len = reduce(lambda x, y: x + len(y), new_buildings_list, 0) if total_len == 0: print "No subsidized buildings" return new_buildings = pd.concat(new_buildings_list) print "Built {} total subsidized buildings".format(len(new_buildings)) print " Total subsidy: ${:,.2f}".format(-1 * new_buildings.max_profit.sum()) print " Total subsidized units: {:.0f}".\ format(new_buildings.residential_units.sum()) new_buildings["subsidized"] = True new_buildings["policy_name"] = policy_name summary.add_parcel_output(new_buildings)
def office_developer(feasibility, jobs, buildings, parcels, year, settings, summary, form_to_btype_func, scenario, add_extra_columns_func, parcels_geography, limits_settings): dev_settings = settings['non_residential_developer'] # I'm going to try a new way of computing this because the math the other # way is simply too hard. Basically we used to try and apportion sectors # into the demand for office, retail, and industrial, but there's just so # much dirtyness to the data, for instance 15% of jobs are in residential # buildings, and 15% in other buildings, it's just hard to know how much # to build, we I think the right thing to do is to compute the number of # job spaces that are required overall, and then to apportion that new dev # into the three non-res types with a single set of coefficients all_units = dev.compute_units_to_build( len(jobs), buildings.job_spaces.sum(), dev_settings['kwargs']['target_vacancy']) print "Total units to build = %d" % all_units if all_units <= 0: return for typ in ["Office"]: print "\nRunning for type: ", typ num_units = all_units * float(dev_settings['type_splits'][typ]) targets = [] # now apply limits - limits are assumed to be yearly, apply to an # entire jurisdiction and be in terms of residential_units or # job_spaces if year > 2015 and typ in limits_settings: juris_name = parcels_geography.juris_name.\ reindex(parcels.index).fillna('Other') juris_list = limits_settings[typ].keys() for juris, limit in limits_settings[typ].items(): # the actual target is the limit times the number of years run # so far in the simulation (plus this year), minus the amount # built in previous years - in other words, you get rollover # and development is lumpy current_total = parcels.total_job_spaces[ (juris_name == juris) & (parcels.newest_building > 2015)]\ .sum() target = (year - 2015 + 1) * limit - current_total if target <= 0: print "Already met target for juris = %s" % juris print " target = %d, current_total = %d" %\ (target, current_total) continue targets.append((juris_name == juris, target, juris)) num_units -= target # other cities not in the targets get the remaining target targets.append((~juris_name.isin(juris_list), num_units, "none")) else: # otherwise use all parcels with total number of units targets.append((parcels.index == parcels.index, num_units, "none")) for parcel_mask, target, juris in targets: print "Running developer for %s with target of %d" % \ (str(juris), target) print "Parcels in play:\n", pd.Series(parcel_mask).value_counts() # this was a fairly heinous bug - have to get the building wrapper # again because the buildings df gets modified by the run_developer # method below buildings = orca.get_table('buildings') new_buildings = utils.run_developer( typ.lower(), jobs, buildings, "job_spaces", parcels.parcel_size[parcel_mask], parcels.ave_sqft_per_unit[parcel_mask], parcels.total_job_spaces[parcel_mask], feasibility, year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, residential=False, num_units_to_build=target, profit_to_prob_func=subsidies.profit_to_prob_func, **dev_settings['kwargs']) if new_buildings is not None: new_buildings["subsidized"] = False summary.add_parcel_output(new_buildings)
def residential_developer(feasibility, households, buildings, parcels, year, settings, summary, form_to_btype_func, add_extra_columns_func, parcels_geography, limits_settings, final_year): kwargs = settings['residential_developer'] target_vacancy = pd.read_csv("data/regional_controls.csv", index_col="year").loc[year].st_res_vac num_units = dev.compute_units_to_build( len(households), buildings["residential_units"].sum(), target_vacancy) targets = [] typ = "Residential" # now apply limits - limits are assumed to be yearly, apply to an # entire jurisdiction and be in terms of residential_units or job_spaces if typ in limits_settings: juris_name = parcels_geography.juris_name.\ reindex(parcels.index).fillna('Other') juris_list = limits_settings[typ].keys() for juris, limit in limits_settings[typ].items(): # the actual target is the limit times the number of years run # so far in the simulation (plus this year), minus the amount # built in previous years - in other words, you get rollover # and development is lumpy current_total = parcels.total_residential_units[ (juris_name == juris) & (parcels.newest_building >= 2010)]\ .sum() target = (year - 2010 + 1) * limit - current_total # make sure we don't overshoot the total development of the limit # for the horizon year - for instance, in Half Moon Bay we have # a very low limit and a single development in a far out year can # easily build over the limit for the total simulation max_target = (final_year - 2010 + 1) * limit - current_total if target <= 0: continue targets.append((juris_name == juris, target, max_target, juris)) num_units -= target # other cities not in the targets get the remaining target targets.append((~juris_name.isin(juris_list), num_units, None, "none")) else: # otherwise use all parcels with total number of units targets.append((parcels.index == parcels.index, num_units, None, "none")) for parcel_mask, target, final_target, juris in targets: print "Running developer for %s with target of %d" % \ (str(juris), target) # this was a fairly heinous bug - have to get the building wrapper # again because the buildings df gets modified by the run_developer # method below buildings = orca.get_table('buildings') new_buildings = utils.run_developer( "residential", households, buildings, "residential_units", parcels.parcel_size[parcel_mask], parcels.ave_sqft_per_unit[parcel_mask], parcels.total_residential_units[parcel_mask], feasibility, year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, num_units_to_build=target, profit_to_prob_func=subsidies.profit_to_prob_func, **kwargs) buildings = orca.get_table('buildings') if new_buildings is not None: new_buildings["subsidized"] = False if final_target is not None and new_buildings is not None: # make sure we don't overbuild the target for the whole simulation overshoot = new_buildings.net_units.sum() - final_target if overshoot > 0: index = new_buildings.tail(1).index[0] index = int(index) # make sure we don't get into a negative unit situation overshoot = min(overshoot, buildings.local.loc[index, "residential_units"]) buildings.local.loc[index, "residential_units"] -= overshoot summary.add_parcel_output(new_buildings)
def office_developer(feasibility, jobs, buildings, parcels, year, settings, summary, form_to_btype_func, scenario, add_extra_columns_func, parcels_geography, limits_settings): dev_settings = settings['non_residential_developer'] # I'm going to try a new way of computing this because the math the other # way is simply too hard. Basically we used to try and apportion sectors # into the demand for office, retail, and industrial, but there's just so # much dirtyness to the data, for instance 15% of jobs are in residential # buildings, and 15% in other buildings, it's just hard to know how much # to build, we I think the right thing to do is to compute the number of # job spaces that are required overall, and then to apportion that new dev # into the three non-res types with a single set of coefficients all_units = dev.compute_units_to_build( len(jobs), buildings.job_spaces.sum(), dev_settings['kwargs']['target_vacancy']) print "Total units to build = %d" % all_units if all_units <= 0: return for typ in ["Office"]: print "\nRunning for type: ", typ num_units = all_units * float(dev_settings['type_splits'][typ]) targets = [] # now apply limits - limits are assumed to be yearly, apply to an # entire jurisdiction and be in terms of residential_units or # job_spaces if year > 2015 and typ in limits_settings: juris_name = parcels_geography.juris_name.\ reindex(parcels.index).fillna('Other') juris_list = limits_settings[typ].keys() for juris, limit in limits_settings[typ].items(): # the actual target is the limit times the number of years run # so far in the simulation (plus this year), minus the amount # built in previous years - in other words, you get rollover # and development is lumpy current_total = parcels.total_job_spaces[ (juris_name == juris) & (parcels.newest_building > 2015)]\ .sum() target = (year - 2015 + 1) * limit - current_total if target <= 0: print "Already met target for juris = %s" % juris print " target = %d, current_total = %d" %\ (target, current_total) continue targets.append((juris_name == juris, target, juris)) num_units -= target # other cities not in the targets get the remaining target targets.append((~juris_name.isin(juris_list), num_units, "none")) else: # otherwise use all parcels with total number of units targets.append((parcels.index == parcels.index, num_units, "none")) for parcel_mask, target, juris in targets: print "Running developer for %s with target of %d" % \ (str(juris), target) print "Parcels in play:\n", pd.Series(parcel_mask).value_counts() # this was a fairly heinous bug - have to get the building wrapper # again because the buildings df gets modified by the run_developer # method below buildings = orca.get_table('buildings') new_buildings = utils.run_developer( typ.lower(), jobs, buildings, "job_spaces", parcels.parcel_size[parcel_mask], parcels.ave_sqft_per_unit[parcel_mask], parcels.total_job_spaces[parcel_mask], feasibility, year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, residential=False, num_units_to_build=target, profit_to_prob_func=subsidies.profit_to_prob_func, **dev_settings['kwargs']) if new_buildings is not None: new_buildings["subsidized"] = False summary.add_parcel_output(new_buildings)
def residential_developer(feasibility, households, buildings, parcels, year, settings, summary, form_to_btype_func, add_extra_columns_func, parcels_geography, limits_settings, final_year, regional_controls): kwargs = settings['residential_developer'] rc = regional_controls.to_frame() target_vacancy = rc.loc[year].st_res_vac num_units = dev.compute_units_to_build( len(households), buildings["residential_units"].sum(), target_vacancy) targets = [] typ = "Residential" # now apply limits - limits are assumed to be yearly, apply to an # entire jurisdiction and be in terms of residential_units or job_spaces if typ in limits_settings: juris_name = parcels_geography.juris_name.\ reindex(parcels.index).fillna('Other') juris_list = limits_settings[typ].keys() for juris, limit in limits_settings[typ].items(): # the actual target is the limit times the number of years run # so far in the simulation (plus this year), minus the amount # built in previous years - in other words, you get rollover # and development is lumpy current_total = parcels.total_residential_units[ (juris_name == juris) & (parcels.newest_building >= 2010)]\ .sum() target = (year - 2010 + 1) * limit - current_total # make sure we don't overshoot the total development of the limit # for the horizon year - for instance, in Half Moon Bay we have # a very low limit and a single development in a far out year can # easily build over the limit for the total simulation max_target = (final_year - 2010 + 1) * limit - current_total if target <= 0: continue targets.append((juris_name == juris, target, max_target, juris)) num_units -= target # other cities not in the targets get the remaining target targets.append((~juris_name.isin(juris_list), num_units, None, "none")) else: # otherwise use all parcels with total number of units targets.append((parcels.index == parcels.index, num_units, None, "none")) for parcel_mask, target, final_target, juris in targets: print "Running developer for %s with target of %d" % \ (str(juris), target) # this was a fairly heinous bug - have to get the building wrapper # again because the buildings df gets modified by the run_developer # method below buildings = orca.get_table('buildings') new_buildings = utils.run_developer( "residential", households, buildings, "residential_units", parcels.parcel_size[parcel_mask], parcels.ave_sqft_per_unit[parcel_mask], parcels.total_residential_units[parcel_mask], feasibility, year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, num_units_to_build=target, profit_to_prob_func=subsidies.profit_to_prob_func, **kwargs) buildings = orca.get_table('buildings') if new_buildings is not None: new_buildings["subsidized"] = False if final_target is not None and new_buildings is not None: # make sure we don't overbuild the target for the whole simulation overshoot = new_buildings.net_units.sum() - final_target if overshoot > 0: index = new_buildings.tail(1).index[0] index = int(index) # make sure we don't get into a negative unit situation current_units = buildings.local.loc[index, "residential_units"] # only can reduce by as many units as we have overshoot = min(overshoot, current_units) # used below - this is the pct we need to reduce the building overshoot_pct = \ (current_units - overshoot) / float(current_units) buildings.local.loc[index, "residential_units"] -= overshoot # we also need to fix the other columns so they make sense for col in ["residential_sqft", "building_sqft", "deed_restricted_units"]: val = buildings.local.loc[index, col] # reduce by pct but round to int buildings.local.loc[index, col] = int(val * overshoot_pct) summary.add_parcel_output(new_buildings)
def run_subsidized_developer(feasibility, parcels, buildings, households, acct_settings, settings, account, year, form_to_btype_func, add_extra_columns_func, summary, create_deed_restricted=False): """ The subsidized residential developer model. Parameters ---------- feasibility : DataFrame A DataFrame that is returned from run_feasibility for a given form parcels : DataFrameWrapper The standard parcels DataFrameWrapper (mostly just for run_developer) buildings : DataFrameWrapper The standard buildings DataFrameWrapper (passed to run_developer) households : DataFrameWrapper The households DataFrameWrapper (passed to run_developer) acct_settings : Dict A dictionary of settings to parameterize the model. Needs these keys: sending_buildings_subaccount_def - maps buildings to subaccounts receiving_buildings_filter - filter for eligible buildings settings : Dict The overall settings account : Account The Account object to use for subsidization year : int The current simulation year (will be added as metadata) form_to_btype_func : function Passed through to run_developer add_extra_columns_func : function Passed through to run_developer summary : Summary Used to add parcel summary information create_deed_restricted : bool Bool for whether to create deed restricted units with the subsidies or not. The logic at the time of this writing is to keep track of partial units so that when partial units sum to greater than a unit, that unit will be deed restricted. Returns ------- Nothing Subsidized residential developer is designed to run before the normal residential developer - it will prioritize the developments we're subsidizing (although this is not strictly required - running this model after the market rate developer will just create a temporarily larger supply of units, which will probably create less market rate development in the next simulated year) the steps for subsidizing are essentially these 1 run feasibility with only_built set to false so that the feasibility of unprofitable units are recorded 2 temporarily filter to ONLY unprofitable units to check for possible subsidized units (normal developer takes care of market-rate units) 3 compute the number of units in these developments 4 divide cost by number of units in order to get the subsidy per unit 5 filter developments to parcels in "receiving zone" similar to the way we identified "sending zones" 6 iterate through subaccounts one at a time as subsidy will be limited to available funds in the subaccount (usually by jurisdiction) 7 sort ascending by subsidy per unit so that we minimize subsidy (but total subsidy is equivalent to total building cost) 8 cumsum the total subsidy in the buildings and locate the development where the subsidy is less than or equal to the amount in the account - filter to only those buildings (these will likely be built) 9 pass the results as "feasible" to run_developer - this is sort of a boundary case of developer but should run OK 10 for those developments that get built, make sure to subtract from account and keep a record (on the off chance that demand is less than the subsidized units, run through the standard code path, although it's very unlikely that there would be more subsidized housing than demand) """ # step 2 feasibility = feasibility.replace([np.inf, -np.inf], np.nan) feasibility = feasibility[feasibility.max_profit < 0] # step 3 feasibility['ave_sqft_per_unit'] = parcels.ave_sqft_per_unit feasibility['residential_units'] = \ np.floor(feasibility.residential_sqft / feasibility.ave_sqft_per_unit) # step 3B # can only add units - don't subtract units - this is an approximation # of the calculation that will be used to do this in the developer model feasibility = feasibility[ feasibility.residential_units > feasibility.total_residential_units] # step 3C # towards the end, because we're about to sort by subsidy per unit, some # large projects never get built, because it could be a 100 unit project # times a 500k subsidy per unit. thus we're going to try filtering by # the maximum subsidy for a single development here feasibility = feasibility[feasibility.max_profit > -50*1000000] # step 4 feasibility['subsidy_per_unit'] = \ -1 * feasibility['max_profit'] / feasibility['residential_units'] # assumption that even if the developer says this property is almost # profitable, even the administration costs are likely to cost at least # 10k / unit feasibility['subsidy_per_unit'] = feasibility.subsidy_per_unit.clip(10000) # step 5 if "receiving_buildings_filter" in acct_settings: feasibility = feasibility.\ query(acct_settings["receiving_buildings_filter"]) else: # otherwise all buildings are valid pass new_buildings_list = [] sending_bldgs = acct_settings["sending_buildings_subaccount_def"] feasibility["regional"] = 1 feasibility["subaccount"] = feasibility.eval(sending_bldgs) # step 6 for subacct, amount in account.iter_subaccounts(): print "Subaccount: ", subacct df = feasibility[feasibility.subaccount == subacct] print "Number of feasible projects in receiving zone:", len(df) if len(df) == 0: continue # step 7 df = df.sort(columns=['subsidy_per_unit'], ascending=True) # df.to_csv('subsidized_units_%d_%s_%s.csv' % # (orca.get_injectable("year"), account.name, subacct)) # step 8 print "Amount in subaccount: ${:,.2f}".format(amount) num_bldgs = int((-1*df.max_profit).cumsum().searchsorted(amount)) if num_bldgs == 0: continue # technically we only build these buildings if there's demand # print "Building {:d} subsidized buildings".format(num_bldgs) df = df.iloc[:int(num_bldgs)] df.columns = pd.MultiIndex.from_tuples( [("residential", col) for col in df.columns]) # disable stdout since developer is a bit verbose for this use case sys.stdout, old_stdout = StringIO(), sys.stdout kwargs = settings['residential_developer'] # step 9 new_buildings = utils.run_developer( "residential", households, buildings, "residential_units", parcels.parcel_size, parcels.ave_sqft_per_unit, parcels.total_residential_units, orca.DataFrameWrapper("feasibility", df), year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, **kwargs) sys.stdout = old_stdout buildings = orca.get_table("buildings") if new_buildings is None: continue # keep track of partial subsidized untis so that we always get credit # for a partial unit, even if it's not built in this specific building partial_subsidized_units = 0 # step 10 for index, new_building in new_buildings.iterrows(): amt = new_building.max_profit metadata = { "description": "Developing subsidized building", "year": year, "residential_units": new_building.residential_units, "building_id": index } account.add_transaction(amt, subaccount=subacct, metadata=metadata) if create_deed_restricted: revenue_per_unit = new_building.building_revenue / \ new_building.residential_units total_subsidy = abs(new_building.max_profit) subsidized_units = total_subsidy / revenue_per_unit + \ partial_subsidized_units # right now there are inclusionary requirements already_subsidized_units = new_building.deed_restricted_units # get remainder partial_subsidized_units = subsidized_units % 1 # round off for now subsidized_units = int(subsidized_units) + \ already_subsidized_units buildings.local.loc[index, "deed_restricted_units"] =\ int(round(subsidized_units)) print "Amount left after subsidy: ${:,.2f}".\ format(account.total_transactions_by_subacct(subacct)) new_buildings_list.append(new_buildings) total_len = reduce(lambda x, y: x+len(y), new_buildings_list, 0) if total_len == 0: print "No subsidized buildings" return new_buildings = pd.concat(new_buildings_list) print "Built {} total subsidized buildings".format(len(new_buildings)) print " Total subsidy: ${:,.2f}".format( -1*new_buildings.max_profit.sum()) print " Total subsidized units: {:.0f}".\ format(new_buildings.residential_units.sum()) new_buildings["subsidized"] = True summary.add_parcel_output(new_buildings)
def residential_developer(feasibility, households, buildings, parcels, year, settings, summary, form_to_btype_func, add_extra_columns_func, parcels_geography, limits_settings, final_year, regional_controls): kwargs = settings['residential_developer'] rc = regional_controls.to_frame() target_vacancy = rc.loc[year].st_res_vac num_units = dev.compute_units_to_build( len(households), buildings["residential_units"].sum(), target_vacancy) targets = [] typ = "Residential" # now apply limits - limits are assumed to be yearly, apply to an # entire jurisdiction and be in terms of residential_units or job_spaces if typ in limits_settings: juris_name = parcels_geography.juris_name.\ reindex(parcels.index).fillna('Other') juris_list = limits_settings[typ].keys() for juris, limit in limits_settings[typ].items(): # the actual target is the limit times the number of years run # so far in the simulation (plus this year), minus the amount # built in previous years - in other words, you get rollover # and development is lumpy current_total = parcels.total_residential_units[ (juris_name == juris) & (parcels.newest_building >= 2010)]\ .sum() target = (year - 2010 + 1) * limit - current_total # make sure we don't overshoot the total development of the limit # for the horizon year - for instance, in Half Moon Bay we have # a very low limit and a single development in a far out year can # easily build over the limit for the total simulation max_target = (final_year - 2010 + 1) * limit - current_total if target <= 0: continue targets.append((juris_name == juris, target, max_target, juris)) num_units -= target # other cities not in the targets get the remaining target targets.append((~juris_name.isin(juris_list), num_units, None, "none")) else: # otherwise use all parcels with total number of units targets.append((parcels.index == parcels.index, num_units, None, "none")) for parcel_mask, target, final_target, juris in targets: print("Running developer for %s with target of %d" % (str(juris), target)) # this was a fairly heinous bug - have to get the building wrapper # again because the buildings df gets modified by the run_developer # method below buildings = orca.get_table('buildings') print('Stats of buildings before run_developer(): \n{}'.format( buildings.to_frame()[['deed_restricted_units','preserved_units','inclusionary_units']].sum())) new_buildings = utils.run_developer( "residential", households, buildings, "residential_units", parcels.parcel_size[parcel_mask], parcels.ave_sqft_per_unit[parcel_mask], parcels.total_residential_units[parcel_mask], feasibility, year=year, form_to_btype_callback=form_to_btype_func, add_more_columns_callback=add_extra_columns_func, num_units_to_build=int(target), profit_to_prob_func=subsidies.profit_to_prob_func, **kwargs) print('Stats of buildings before run_developer(): \n{}'.format( buildings.to_frame()[['deed_restricted_units','preserved_units','inclusionary_units']].sum())) buildings = orca.get_table('buildings') if new_buildings is not None: new_buildings["subsidized"] = False if final_target is not None and new_buildings is not None: # make sure we don't overbuild the target for the whole simulation overshoot = new_buildings.net_units.sum() - final_target if overshoot > 0: index = new_buildings.tail(1).index[0] index = int(index) # make sure we don't get into a negative unit situation current_units = buildings.local.loc[index, "residential_units"] # only can reduce by as many units as we have overshoot = min(overshoot, current_units) # used below - this is the pct we need to reduce the building overshoot_pct = \ (current_units - overshoot) / float(current_units) buildings.local.loc[index, "residential_units"] -= overshoot # we also need to fix the other columns so they make sense for col in ["residential_sqft", "building_sqft", "deed_restricted_units", "inclusionary_units", "subsidized_units"]: val = buildings.local.loc[index, col] # reduce by pct but round to int buildings.local.loc[index, col] = int(val * overshoot_pct) # also fix the corresponding columns in new_buildings for col in ["residential_sqft","building_sqft", "residential_units", "deed_restricted_units", "inclusionary_units", "subsidized_units"]: val = new_buildings.loc[index, col] new_buildings.loc[index, col] = int(val * overshoot_pct) for col in ["policy_based_revenue_reduction", "max_profit"]: val = new_buildings.loc[index, col] new_buildings.loc[index, col] = val * overshoot_pct summary.add_parcel_output(new_buildings)