class Window(Results, Cost): data = models.ForeignKey('ComponentBase', related_name='windows') direction = models.ForeignKey(Direction, primary_key=True) new = fields.CostField() old = fields.CostField() maintenance_cost = fields.CostField() repair_cost = fields.CostField() repair_rate = fields.YearField() replacement_cost = fields.CostField() replacement_rate = fields.YearField() def make(self, data, direction): building = data.group.building simulation = data.group.simulation region = data.group.location.state.census_region component = WindowComponent( building.window, simulation.windows.get(direction=direction)) index = lambda cost: data.group.index(cost, 'windows') get = lambda attr: index(getattr(component, attr)) self.new = index(component.cost * component.size) self.old = index(component.old.cost * component.size) self.maintenance_cost = get('maintenance_cost') self.repair_cost = get('repair_cost') * component.size self.replacement_cost = get('replacement_cost') rates = component.rates(region) self.repair_rate = rates.repair self.replacement_rate = rates.replace self.save()
class ComponentBase(Results): group = DataLinkField('Group', 'components') cost = fields.CostField(null=True) delta = fields.CostField(null=True) def make(self, group): self.save() for d in Direction.objects.all(): Window.create(data=self, direction=d) parts = list(self.parts()) self.cost = sum(p.cost for p in parts) self.delta = sum(p.delta for p in parts) self.save() def parts(self): standard = self.group.standard zone = self.group.location.climate_zone(standard) for window in self.windows.all(): yield window if standard.use_overhang(zone): yield self.overhang if standard.use_daylighting: yield self.daylight_system yield self.wall_insulation yield self.roof_insulation
class AssemblyComponentPart(Results): component = models.ForeignKey(AssemblyComponent, related_name='parts') maintenance_cost = fields.CostField() repair_cost = fields.CostField() repair_rate = fields.YearField() replacement_cost = fields.CostField() replacement_rate = fields.YearField() @classmethod def create(cls, component, part): self = cls(component=component) group = component.assembly.group args = (group.building, group.simulation.heating_capacity.into('MBH'), group.simulation.cooling_capacity.into('cton')) index = lambda cost: group.index(cost, 'HVAC') self.maintenance_cost = index(part.maintenance_cost) self.repair_cost = index(part.repair_cost_for(*args)) self.replacement_cost = index(part.replacement_cost_for(*args)) rates = part.rates(group.location.state.census_region) self.repair_rate = rates.repair self.replacement_rate = rates.replace self.save() return self
class GroupData(Results): group = DataLinkField('Group', 'data') esavings = fields.CostField() first = fields.CostField() def make(self, group): self.esavings = group.assemblies.delta + group.components.delta self.first = self.esavings + group.index(group.building.subtotal, 'weighted-average') self.save()
class RowData(Results): row = DataLinkField('Row', 'data') base = fields.CostField() future = fields.CostField(null=True) investment = fields.CostField(null=True) lifecycle = fields.CostField(null=True) maintenance = fields.CostField(null=True) repair = fields.CostField(null=True) replacement = fields.CostField(null=True) credit = fields.CostField(null=True) def make(self, row): building = row.group.building spv = row.group.database.spv cost = lambda year: building.costs.get(year=year).cost base = building.square_feet * sum( cost(year) * spv(year) for year in row.years) self.base = row.group.index(base, 'whitestone') self.save() parts = row.assemblies, row.components for attr in 'maintenance', 'repair', 'replacement', 'credit': setattr(self, attr, sum(getattr(part, attr) for part in parts)) residual = min(r.residual.cost for r in row.options) self.future = row.pv(row.energy.cost + self.base + self.maintenance + self.repair + self.replacement - self.credit - residual) self.investment = (row.group.data.first - residual + self.replacement) self.lifecycle = (row.group.data.first + self.future) self.save()
class Costs(models.Model): row = models.ForeignKey(Row, related_name='costs') period = fields.YearField() lifecycle_cost = fields.CostField() efficiency_cost = fields.CostField() energy_cost = fields.CostField() energy_use = metric.KiloWattHourField() gas_use = uscs.BTUperYearField() electricity_use = uscs.BTUperYearField() class Meta: unique_together = 'row', 'period' def __unicode__(self): return 'Deltas for %s' % self.row
class RoofInsulation(Insulation): data = DataLinkField('ComponentBase', 'roof_insulation') new = fields.CostField() old = fields.CostField() maintenance_cost = fields.CostField() repair_cost = fields.CostField() repair_rate = fields.YearField() replacement_cost = fields.CostField() replacement_rate = fields.YearField() RateModel = BlanketWallInsulation def makewith(self, data, location, building, standard, simulation): sheets = RigidRoofInsulation.sheets(location, building, standard) size = simulation.roof_area.into('ft**2') self.takefrom(data.group, sheets, size, building.roof_insulation) self.save()
class Component(Results): row = DataLinkField('Row', 'components') maintenance = fields.CostField(null=True) repair = fields.CostField(null=True) replacement = fields.CostField(null=True) credit = fields.CostField(null=True) def make(self, row): self.save() parts = row.group.components.parts() totals = [ComponentTotals.create(p, parent=self) for p in parts] self.maintenance = sum(t.maintenance for t in totals) self.repair = sum(t.repair for t in totals) self.replacement = sum(t.replacement for t in totals) self.credit = sum(t.credit for t in totals) self.save()
class Boiler(models.Model): number = models.CharField(max_length=15) description = models.CharField(max_length=255) fuels = models.ManyToManyField(base.Fuel) flow = models.ForeignKey(BoilerFlow, blank=True, null=True) mbh = uscs.MBHField() cost = fields.CostField() class Meta: ordering = 'mbh', @property def cost_per_mbh(self): return self.cost / self.mbh def matching(self, objects, fueled=False): if not fueled: return objects objects = objects.annotate(num=Count('fuels')) objects = objects.filter(num=self.fuels.count()) hasfuel = lambda set, fuel: set.filter(fuels=fuel) return reduce(hasfuel, self.fuels.all(), objects) def bestfit(self, objects, fueled=False): for item in self.matching(objects, fueled): if item.contains(self.mbh): return item def maintenance_cost_for(self, building, mbh, tons): return self.maintenance_cost def repair_cost_for(self, building, mbh, tons): return self.repair_cost * mbh def replacement_cost_for(self, building, mbh, tons): return self.replacement_cost * mbh @cached_property def repair_cost(self): return self.bestfit(BoilerRepair.objects, True).cost @cached_property def maintenance_cost(self): return self.bestfit( BoilerMaintenance.objects.filter(flow=self.flow)).cost @cached_property def replacement_cost(self): try: cost = self.replacements.all()[0].cost except IndexError: cost = self.cost return cost / self.mbh def rates(self, zone): return self.matching(BoilerRates.objects, True).get(zone=zone) def __unicode__(self): fuels = '/'.join(map(str, self.fuels.all())) return '%.2f MBH, %s %s boiler' % (self.mbh, price(self.cost), fuels)
class Assembly(Results): row = DataLinkField('Row', 'assemblies') maintenance = fields.CostField(null=True) repair = fields.CostField(null=True) replacement = fields.CostField(null=True) credit = fields.CostField(null=True) def make(self, row): self.save() components = row.group.assemblies.components.all() parts = chain(*(c.parts.all() for c in components)) totals = [AssemblyPartTotals.create(p, parent=self) for p in parts] self.maintenance = sum(t.maintenance for t in totals) self.repair = sum(t.repair for t in totals) self.replacement = sum(t.replacement for t in totals) self.credit = sum(t.credit for t in totals) self.save()
class AssemblyBase(Results): group = DataLinkField('Group', 'assemblies') cost = fields.CostField(null=True) delta = fields.CostField(null=True) def make(self, group): self.save() components = [ AssemblyComponent.create(self, system) for system in filter(bool, (group.building.heating_system, group.building.cooling_system, group.building.packaged_unit, group.building.energy_supply)) ] self.cost = sum(c.cost for c in components) self.delta = sum(c.delta for c in components) self.save()
class Replace(models.Model): cost = fields.CostField() class Meta: abstract = True ordering = 'cost', def __unicode__(self): return '%s: %s' % (self.unit, price(self.cost))
class Replace(models.Model): unit = models.ForeignKey(PackagedUnit, related_name='replacements') cost = fields.CostField() class Meta: verbose_name_plural = 'replacements' def __unicode__(self): return '%s: %s' % (self.unit, price(self.cost))
class Component(models.Model): number = models.CharField(max_length=15) description = models.CharField(max_length=255) cost = fields.CostField() tons = uscs.TonField() class Meta: abstract = True @classmethod def rates(cls, zone): type = ContentType.objects.get_for_model(cls) return Rates.objects.get(component=type, zone=zone) @cached_property def cost_per_ton(self): return self.cost / self.ton def average_cost(self, tons): return self.marginal_cost(tons) * tons def marginal_cost(self, tons): tons = tons.just(cooling) return (self.a * (tons ** self.b)) * (dollar / cooling) def bestfit(self, set): for item in set: if item.contains(self.tons): return item def maintenance_cost_for(self, building, mbh, tons): return self.maintenance_cost def repair_cost_for(self, building, mbh, tons): return self.repair_cost * tons def replacement_cost_for(self, building, mbh, tons): return self.replacement_cost * tons @cached_property def maintenance_cost(self): return self.bestfit(self.maintenances.objects.all()).cost @cached_property def repair_cost(self): return self.bestfit(self.repairs.objects.all()).cost_per_ton @cached_property def replacement_cost(self): try: cost = self.replacements.all()[0].cost except IndexError: cost = self.cost return cost / self.tons def __unicode__(self): return '%.2f ton, %s' % (self.tons, price(self.cost))
class BoilerReplace(models.Model): unit = models.ForeignKey(Boiler, related_name='replacements') cost = fields.CostField() class Meta: ordering = 'cost', def __unicode__(self): return '%s: %s' % (self.unit, price(self.cost))
class DaylightSystem(Lighting): data = DataLinkField('ComponentBase', 'daylight_system') new = fields.CostField(default=0) old = 0 * dollar maintenance_cost = fields.CostField() repair_cost = fields.CostField() repair_rate = fields.YearField() replacement_cost = fields.CostField() replacement_rate = fields.YearField() def make(self, data): building = data.group.building systems = lighting.DaylightSystem.objects.order_by('fixtures') try: component = systems.filter(fixtures__gte=building.fixtures)[0] except IndexError: component = systems[-1] self.takefrom(data.group, building.square_feet, component) self.save()
class BoilerMaintenance(Upkeep): flow = models.ForeignKey(BoilerFlow, blank=True, null=True) cost = fields.CostField() class Meta: ordering = 'flow', 'cost' def __unicode__(self): return super(BoilerMaintenance, self).__unicode__() + ' (%s)' % (self.flow or 'Electric')
class AssemblyComponent(Results, Cost): assembly = models.ForeignKey(AssemblyBase, related_name='components') old = fields.CostField() new = fields.CostField() @classmethod def create(cls, assembly, system): self = cls(assembly=assembly) old = system.original_cost(assembly.group.building) new = system.cost_for( assembly.group.building, assembly.group.simulation.heating_capacity.into('MBH'), assembly.group.simulation.cooling_capacity.into('cton')) self.new = assembly.group.index(new, 'HVAC') self.old = assembly.group.index(old, 'HVAC') self.save() for part in system: AssemblyComponentPart.create(self, part).save() return self
class Building(models.Model): type = models.CharField(max_length=6, primary_key=True) name = models.CharField(max_length=50) life = fields.YearField(verbose_name='Service Life') roof = models.ForeignKey(Roof) wall = models.ForeignKey(Wall) stories = models.PositiveSmallIntegerField() story_height = uscs.FootField(help_text='(in feet)') width = uscs.FootField(help_text='East/West (in feet)') length = uscs.FootField(help_text='North/South (in feet)') blanket_wall_insulation = models.ForeignKey(BlanketWallInsulation, blank=True, null=True) rigid_wall_insulation = models.ForeignKey(RigidWallInsulation, blank=True, null=True) roof_insulation = models.ForeignKey(RigidRoofInsulation, blank=True, null=True) window = models.ForeignKey(Window) energy_supply = models.ForeignKey(EnergySupply, blank=True, null=True) heating_system = models.ForeignKey(HeatingSystem, blank=True, null=True) cooling_system = models.ForeignKey(CoolingSystem, blank=True, null=True) packaged_unit = models.ForeignKey(PackagedUnit, blank=True, null=True) fixtures = models.FloatField( help_text='(in fixtures per 1000 square feet)') subtotal = fields.CostField() release_year = fields.YearField() class Meta: ordering = 'type', @cached_property def perimiter(self): return (2 * self.width) + (2 * self.height) @cached_property def height(self): return self.story_height * self.stories @cached_property def square_feet(self): return self.footprint * self.stories @cached_property def footprint(self): return self.width * self.height def __unicode__(self): return self.name.title()
class Residual(Results): row = DataLinkField('Row', 'residual') cost = fields.CostField() def make(self, row): building = row.group.building if row.period > building.life: return 0 * dollar left = building.life - row.period self.cost = (float(left) / building.life.item()) * row.group.data.first self.save()
class Overhang(Lighting): data = DataLinkField('ComponentBase', 'overhang') new = fields.CostField() old = 0 * dollar maintenance_cost = fields.CostField() repair_cost = fields.CostField() repair_rate = fields.YearField() replacement_cost = fields.CostField() replacement_rate = fields.YearField() index = 'weighted-average' def make(self, data): simulation = data.group.simulation standard = data.group.standard zone = data.group.location.climate_zone(standard) if not standard.use_overhang(zone): return size = simulation.overhang_area.into('ft**2') component = standard.overhang(zone) self.takefrom(data.group, size, component) self.save()
class Component(ConstantMRRModel): cost = uscs.CostPerSquareFootField() maintenance_cost = fields.CostField(default=0) repair_cost = uscs.CostPerSquareFootField() replacement_cost = uscs.CostPerSquareFootField() class Meta: abstract = True def __iter__(self): return iter([self])
class Base(models.Model): set = models.ForeignKey(ResultSet) building = models.ForeignKey('structures.Building') location = models.ForeignKey('locations.Location') period = fields.YearField() residual = fields.CostField() class Meta: unique_together = 'building', 'location', 'period' def __unicode__(self): return 'Base for %s, %s, %s' % (self.building, self.location, self.period)
class FurnaceUpkeep(heating.Upkeep): fuel = models.ForeignKey(base.Fuel) cost = fields.CostField() mbh = uscs.MBHField() class Meta: abstract = True @cached_property def cost_per_mbh(self): return self.cost / self.mbh def adjusted_cost(self, mbh): return self.cost_per_mbh * mbh
class WallInsulation(Insulation): data = DataLinkField('ComponentBase', 'wall_insulation') new = fields.CostField() old = fields.CostField() maintenance_cost = fields.CostField() repair_cost = fields.CostField() repair_rate = fields.YearField() replacement_cost = fields.CostField() replacement_rate = fields.YearField() RateModel = BlanketWallInsulation def makewith(self, data, location, building, standard, simulation): r = BlanketWallInsulation.r(standard, building, location.climate_zone(standard)) inner = tuple( BlanketWallInsulation.sheets(location, building, standard)) r -= sum((i.r for i in inner if i.r), 0) outer = RigidWallInsulation.sheets(location, building, standard, r) size = simulation.wall_area.into('ft**2') self.takefrom(data.group, chain(inner, outer), size, building.blanket_wall_insulation, building.rigid_wall_insulation) self.save()
class Energy(Results): row = DataLinkField('Row', 'energy') use = metric.KiloWattHourField(null=True) cost = fields.CostField(null=True) fuels = assemblies.Fuel.objects.filter(name__in=('Gas', 'Electric')) def make(self, row): self.save() fuels = [Fuel.create(data=self, fuel=fuel) for fuel in self.fuels] self.use = sum(f.use for f in fuels) self.cost = sum(f.cost for f in fuels) self.save() @cached_property def usage(self): return dict((f.fuel, f.use) for f in self.fueldatas.all())
class Upkeep(models.Model): min = uscs.TonField() max = uscs.TonField(blank=True, null=True, help_text='Leave blank for none.') cost = fields.CostField() class Meta: abstract = True ordering = 'cost', def contains(self, tons): return self.min <= tons <= (self.max or tons) def __unicode__(self): if self.max: return '%s to %s tons: %s' % (self.min, self.max, price(self.cost)) return 'More than %s tons: %s' % (self.min, price(self.cost))
class Fuel(Results): data = models.ForeignKey(Energy, related_name='fueldatas') fuel = models.ForeignKey('assemblies.Fuel') cost = fields.CostField() use = metric.KiloWattHourField() def make(self, data, fuel): from relator.constants.models import Tariff, PriceIndex row = self.data.row year = row.period state = row.group.location.state deflator = float(row.group.database.deflator) enduses = row.group.simulation.fuel_enduses rate = Tariff.objects.get(fuel=self.fuel, state=state).tariff upv = PriceIndex.upv(self.fuel, state.census_region, year, deflator) self.use = enduses.get(fuel=self.fuel).total.into('kWh') self.cost = rate * upv * self.use self.save()
class CoolingSystem(models.Model): number = models.CharField(max_length=15) description = models.CharField(max_length=255) cost = fields.CostField() tons = uscs.TonField() square_feet = uscs.SquareFootField() fan_coil = models.ForeignKey(FanCoil, blank=True, null=True) chiller = models.ForeignKey(Chiller, blank=True, null=True) cooling_tower = models.ForeignKey(CoolingTower, blank=True, null=True) def __iter__(self): return iter(self.components) @cached_property def components(self): return tuple(filter(bool, (self.fan_coil, self.chiller, self.cooling_tower))) @cached_property def initial_fixed_cost(self): return self.cost - sum(c.average_cost(self.tons) for c in self.components) @cached_property def fixed_cost_per_square_foot(self): return self.initial_fixed_cost / self.square_feet def fixed_cost(self, building): return self.fixed_cost_per_square_foot * building.square_feet def cost_for(self, building, mbh, tons): tons = tons.into(cooling) cost_per_ton = sum(c.marginal_cost(tons) for c in self.components) return (cost_per_ton * tons) + self.fixed_cost(building) def original_cost(self, building): return self.cost_for(building, None, self.tons) def __unicode__(self): return u'%s ft\u00B2, %.2f ton, %s unit' % (intcomma(self.square_feet.just('ft**2')), self.tons, price(self.cost))
class EnergySupply(models.Model): number = models.CharField(max_length=15) description = models.CharField(max_length=255) mbh = uscs.MBHField() cost = fields.CostField(help_text='Boiler cost not included') square_feet = uscs.SquareFootField() boilers = models.ManyToManyField(Boiler) class Meta: ordering = 'mbh', verbose_name_plural = 'energy supplies' def __iter__(self): return iter(self.boilers.all()) @property def cost_per_mbh(self): return self.cost / self.mbh @cached_property def fixed_cost_per_square_foot(self): return self.cost / self.square_feet def fixed_cost(self, building): return self.fixed_cost_per_square_foot * building.square_feet def cost_for(self, building, mbh, tons): mbh = mbh.into(MBH) cost_per_mbh = sum(boiler.cost_per_mbh for boiler in self.boilers.all()) return (cost_per_mbh * mbh) + self.fixed_cost(building) def original_cost(self, building): return self.cost_for(building, self.mbh, None) def __unicode__(self): return u'%s ft\u00B2, %.2f MBH, %s unit' % (intcomma( self.square_feet.just('ft**2')), self.mbh, price(self.cost))