From a92e72f71fa67816bf52701b62b0a02595efdf2f Mon Sep 17 00:00:00 2001 From: Joy Zhang Date: Fri, 16 Aug 2024 17:55:19 -0700 Subject: [PATCH] debug ideal clarifier --- qsdsan/sanunits/_clarifier.py | 1117 +++++++++++++------------- qsdsan/sanunits/_sludge_treatment.py | 443 +++++----- 2 files changed, 771 insertions(+), 789 deletions(-) diff --git a/qsdsan/sanunits/_clarifier.py b/qsdsan/sanunits/_clarifier.py index 61150653..2e1b93e5 100644 --- a/qsdsan/sanunits/_clarifier.py +++ b/qsdsan/sanunits/_clarifier.py @@ -136,12 +136,12 @@ class FlatBottomCircularClarifier(SanUnit): _N_ins = 1 _N_outs = 3 - # Costs - wall_concrete_unit_cost = 1081.73 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) - slab_concrete_unit_cost = 582.48 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) - stainless_steel_unit_cost=1.8 # Alibaba. Brushed Stainless Steel Plate 304. https://www.alibaba.com/product-detail/brushed-stainless-steel-plate-304l-stainless_1600391656401.html?spm=a2700.details.0.0.230e67e6IKwwFd + # # Costs + # wall_concrete_unit_cost = 1081.73 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) + # slab_concrete_unit_cost = 582.48 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) + # stainless_steel_unit_cost=1.8 # Alibaba. Brushed Stainless Steel Plate 304. https://www.alibaba.com/product-detail/brushed-stainless-steel-plate-304l-stainless_1600391656401.html?spm=a2700.details.0.0.230e67e6IKwwFd - pumps = ('ras', 'was',) + # pumps = ('ras', 'was',) def __init__(self, ID='', ins=None, outs=(), thermo=None, init_with='WasteStream', underflow=2000, wastage=385, @@ -552,261 +552,273 @@ def dy_dt(t, QC_ins, QC, dQC_ins): self._ODE = dy_dt - _units = { - 'Number of clarifiers': 'ea', - 'Volumetric flow': 'm3/day', - 'Clarifier depth': 'm', - 'Surface area': 'm2', - 'Clarifier diameter': 'm', - 'Clarifier volume': 'm3', - 'Design solids loading rate': 'kg/m2/hr', - 'Surface overflow rate': 'm3/day/m2', - 'Hydraulic Retention Time': 'hr', - 'Center feed depth': 'm', - 'Downward flow velocity': 'm/hr', - 'Center feed diameter': 'm', - 'Volume of concrete wall': 'm3', - 'Stainless steel': 'kg', - 'Pump pipe stainless steel' : 'kg', - 'Pump stainless steel': 'kg', - 'Number of pumps': 'ea' - } + # _units = { + # 'Number of clarifiers': 'ea', + # 'Volumetric flow': 'm3/day', + # 'Clarifier depth': 'm', + # 'Surface area': 'm2', + # 'Clarifier diameter': 'm', + # 'Clarifier volume': 'm3', + # 'Design solids loading rate': 'kg/m2/hr', + # 'Surface overflow rate': 'm3/day/m2', + # 'Hydraulic Retention Time': 'hr', + # 'Center feed depth': 'm', + # 'Downward flow velocity': 'm/hr', + # 'Center feed diameter': 'm', + # 'Volume of concrete wall': 'm3', + # 'Stainless steel': 'kg', + # 'Pump pipe stainless steel' : 'kg', + # 'Pump stainless steel': 'kg', + # 'Number of pumps': 'ea' + # } - def _design_pump(self): - ID, pumps = self.ID, self.pumps + # def _design_pump(self): + # ID, pumps = self.ID, self.pumps - self._ras.copy_like(self.outs[1]) - self._was.copy_like(self.outs[2]) + # self._ras.copy_like(self.outs[1]) + # self._was.copy_like(self.outs[2]) - ins_dct = { - 'ras': self._ras, - 'was': self._was, - } + # ins_dct = { + # 'ras': self._ras, + # 'was': self._was, + # } - D = self.design_results + # D = self.design_results - ras_flow = self._ras.get_total_flow('m3/hr') - was_flow = self._was.get_total_flow('m3/hr') + # ras_flow = self._ras.get_total_flow('m3/hr') + # was_flow = self._was.get_total_flow('m3/hr') - ras_flow_u = ras_flow/D['Number of clarifiers']*0.00634 - was_flow_u = was_flow/D['Number of clarifiers']*0.00634 + # ras_flow_u = ras_flow/D['Number of clarifiers']*0.00634 + # was_flow_u = was_flow/D['Number of clarifiers']*0.00634 - Q_mgd = { - 'ras': ras_flow_u, - 'was': was_flow_u, - } + # Q_mgd = { + # 'ras': ras_flow_u, + # 'was': was_flow_u, + # } - type_dct = dict.fromkeys(pumps, 'sludge') - inputs_dct = dict.fromkeys(pumps, (1,)) + # type_dct = dict.fromkeys(pumps, 'sludge') + # inputs_dct = dict.fromkeys(pumps, (1,)) - for i in pumps: - if hasattr(self, f'{i}_pump'): - p = getattr(self, f'{i}_pump') - setattr(p, 'add_inputs', inputs_dct[i]) - else: - ID = f'{ID}_{i}' - capacity_factor=1 - pump = WWTpump( - ID=ID, ins=ins_dct[i], thermo = self.thermo, pump_type=type_dct[i], - Q_mgd=Q_mgd[i], add_inputs=inputs_dct[i], - capacity_factor=capacity_factor, - include_pump_cost=True, - include_building_cost=False, - include_OM_cost=True, - ) - setattr(self, f'{i}_pump', pump) - - pipe_ss, pump_ss = 0., 0. - for i in pumps: - p = getattr(self, f'{i}_pump') - p.simulate() - p_design = p.design_results - pipe_ss += p_design['Pump pipe stainless steel'] - pump_ss += p_design['Pump stainless steel'] - return pipe_ss, pump_ss + # for i in pumps: + # if hasattr(self, f'{i}_pump'): + # p = getattr(self, f'{i}_pump') + # setattr(p, 'add_inputs', inputs_dct[i]) + # else: + # ID = f'{ID}_{i}' + # capacity_factor=1 + # pump = WWTpump( + # ID=ID, ins=ins_dct[i], thermo = self.thermo, pump_type=type_dct[i], + # Q_mgd=Q_mgd[i], add_inputs=inputs_dct[i], + # capacity_factor=capacity_factor, + # include_pump_cost=True, + # include_building_cost=False, + # include_OM_cost=True, + # ) + # setattr(self, f'{i}_pump', pump) + + # pipe_ss, pump_ss = 0., 0. + # for i in pumps: + # p = getattr(self, f'{i}_pump') + # p.simulate() + # p_design = p.design_results + # pipe_ss += p_design['Pump pipe stainless steel'] + # pump_ss += p_design['Pump stainless steel'] + # return pipe_ss, pump_ss - def _design(self): - - self._mixed.mix_from(self.ins) - mixed = self._mixed - D = self.design_results - - # Number of clarifiers based on tentative suggestions by Jeremy - # (would be verified through collaboration with industry) - total_flow = (mixed.get_total_flow('m3/hr')*24)/3785 # in MGD - if total_flow <= 3: - D['Number of clarifiers'] = 2 - elif total_flow > 3 and total_flow <= 8: - D['Number of clarifiers'] = 3 - elif total_flow > 8 and total_flow <=20: - D['Number of clarifiers'] = 4 - else: - D['Number of clarifiers'] = 4 - total_flow -= 20 - D['Number of clarifiers'] += np.ceil(total_flow/20) + # def _design(self): + + # self._mixed.mix_from(self.ins) + # mixed = self._mixed + # D = self.design_results + + # # Number of clarifiers based on tentative suggestions by Jeremy + # # (would be verified through collaboration with industry) + # total_flow = (mixed.get_total_flow('m3/hr')*24)/3785 # in MGD + # if total_flow <= 3: + # D['Number of clarifiers'] = 2 + # elif total_flow > 3 and total_flow <= 8: + # D['Number of clarifiers'] = 3 + # elif total_flow > 8 and total_flow <=20: + # D['Number of clarifiers'] = 4 + # else: + # D['Number of clarifiers'] = 4 + # total_flow -= 20 + # D['Number of clarifiers'] += np.ceil(total_flow/20) - D['Volumetric flow'] = (mixed.get_total_flow('m3/hr')*24)/D['Number of clarifiers'] #m3/day - - # Sidewater depth of a cylindrical clarifier lies between 4-5 m (MOP 8) - D['Clarifier depth'] = self._h # in m - - # Area of clarifier - # D['Surface area'] = solids_clarifier/D['Solids loading rate'] #m2 - D['Surface area'] = self._A/D['Number of clarifiers'] - D['Clarifier diameter'] = np.sqrt(4*D['Surface area']/np.pi) # in m - D['Clarifier volume'] = D['Surface area']*D['Clarifier depth'] # in m3 - - # Checks on SLR,, SOR, and HRT - - D['Design solids loading rate'] = self._slr # kg/(m2*hr) - - total_solids = mixed.get_TSS()*mixed.get_total_flow('m3/hr')/1000 # in kg/hr (mg/l * m3/hr) - solids_clarifier = total_solids/D['Number of clarifiers'] # in kg/hr - simulated_slr = solids_clarifier/D['Surface area'] # in kg/(m2*hr) - - # Consult Joy on the margin or error - if simulated_slr < 0.8*D['Design solids loading rate'] or simulated_slr > 1.2*D['Design solids loading rate']: - design_slr = D['Design solids loading rate'] - warn(f'Solids loading rate = {simulated_slr} is not within 20% of the recommended design level of {design_slr} kg/hr/m2') - - # Check on SLR [3, 4, 5] - if simulated_slr > 14: - warn(f'Solids loading rate = {simulated_slr} is above recommended level of 14 kg/hr/m2') - - # Check on SOR [3, 4, 5] - D['Surface overflow rate'] = D['Volumetric flow']/D['Surface area'] # in m3/m2/hr - if D['Surface overflow rate'] > 49: - sor = D['Surface overflow rate'] - warn(f'Surface overflow rate = {sor} is above recommended level of 49 m3/day/m2') - - # HRT - D['Hydraulic Retention Time'] = D['Clarifier volume']*24/D['Volumetric flow'] # in hr - - # Clarifiers can be center feed or peripheral feed. The design here is for the more commonly deployed center feed. - # Depth of the center feed lies between 30-75% of sidewater depth. [2] - D['Center feed depth'] = 0.5*D['Clarifier depth'] - # Criteria for downward velocity of flow determine - D['Downward flow velocity'] = self._downward_flow_velocity # in m/hr - Center_feed_area = (D['Volumetric flow']/24)/D['Downward flow velocity'] # in m2 - D['Center feed diameter'] = np.sqrt(4*Center_feed_area/np.pi) - - #Sanity check: Diameter of the center feed lies between 20-25% of tank diameter [2] - if D['Center feed diameter'] < 0.20*D['Clarifier diameter'] or D['Center feed diameter'] > 0.25*D['Clarifier diameter']: - cf_dia = D['Center feed diameter'] - tank_dia = D['Clarifier diameter'] - warn(f'Diameter of the center feed does not lie between 20-25% of tank diameter. It is {cf_dia*100/tank_dia} % of tank diameter') + # D['Volumetric flow'] = (mixed.get_total_flow('m3/hr')*24)/D['Number of clarifiers'] #m3/day + + # # Sidewater depth of a cylindrical clarifier lies between 4-5 m (MOP 8) + # D['Clarifier depth'] = self._h # in m + + # # Area of clarifier + # # D['Surface area'] = solids_clarifier/D['Solids loading rate'] #m2 + # D['Surface area'] = self._A/D['Number of clarifiers'] + # D['Clarifier diameter'] = np.sqrt(4*D['Surface area']/np.pi) # in m + # D['Clarifier volume'] = D['Surface area']*D['Clarifier depth'] # in m3 + + # # Checks on SLR,, SOR, and HRT + + # D['Design solids loading rate'] = self._slr # kg/(m2*hr) + + # total_solids = mixed.get_TSS()*mixed.get_total_flow('m3/hr')/1000 # in kg/hr (mg/l * m3/hr) + # solids_clarifier = total_solids/D['Number of clarifiers'] # in kg/hr + # simulated_slr = solids_clarifier/D['Surface area'] # in kg/(m2*hr) + + # # Consult Joy on the margin or error + # if simulated_slr < 0.8*D['Design solids loading rate'] or simulated_slr > 1.2*D['Design solids loading rate']: + # design_slr = D['Design solids loading rate'] + # warn(f'Solids loading rate = {simulated_slr} is not within 20% of the recommended design level of {design_slr} kg/hr/m2') + + # # Check on SLR [3, 4, 5] + # if simulated_slr > 14: + # warn(f'Solids loading rate = {simulated_slr} is above recommended level of 14 kg/hr/m2') + + # # Check on SOR [3, 4, 5] + # D['Surface overflow rate'] = D['Volumetric flow']/D['Surface area'] # in m3/m2/hr + # if D['Surface overflow rate'] > 49: + # sor = D['Surface overflow rate'] + # warn(f'Surface overflow rate = {sor} is above recommended level of 49 m3/day/m2') + + # # HRT + # D['Hydraulic Retention Time'] = D['Clarifier volume']*24/D['Volumetric flow'] # in hr + + # # Clarifiers can be center feed or peripheral feed. The design here is for the more commonly deployed center feed. + # # Depth of the center feed lies between 30-75% of sidewater depth. [2] + # D['Center feed depth'] = 0.5*D['Clarifier depth'] + # # Criteria for downward velocity of flow determine + # D['Downward flow velocity'] = self._downward_flow_velocity # in m/hr + # Center_feed_area = (D['Volumetric flow']/24)/D['Downward flow velocity'] # in m2 + # D['Center feed diameter'] = np.sqrt(4*Center_feed_area/np.pi) + + # #Sanity check: Diameter of the center feed lies between 20-25% of tank diameter [2] + # if D['Center feed diameter'] < 0.20*D['Clarifier diameter'] or D['Center feed diameter'] > 0.25*D['Clarifier diameter']: + # cf_dia = D['Center feed diameter'] + # tank_dia = D['Clarifier diameter'] + # warn(f'Diameter of the center feed does not lie between 20-25% of tank diameter. It is {cf_dia*100/tank_dia} % of tank diameter') - # Amount of concrete required - D_tank = D['Clarifier depth']*39.37 # m to inches - # Thickness of the wall concrete, [m]. Default to be minimum of 1 ft with 1 in added for every ft of depth over 12 ft. (Brian's code) - thickness_concrete_wall = (1 + max(D_tank-12, 0)/12)*0.3048 # from feet to m - inner_diameter = D['Clarifier diameter'] - outer_diameter = inner_diameter + 2*thickness_concrete_wall - D['Volume of concrete wall'] = (np.pi*D['Clarifier depth']/4)*(outer_diameter**2 - inner_diameter**2) - - # Concrete slab thickness, [ft], default to be 2 in thicker than the wall thickness. (Brian's code) - thickness_concrete_slab = thickness_concrete_wall + (2/12)*0.3048 # from inch to m - # From Brian's code - D['Volume of concrete slab'] = (thickness_concrete_slab + thickness_concrete_wall)*D['Surface area'] - - # Amount of metal required for center feed - thickness_metal_wall = 0.3048 # equal to 1 feet, in m (!! NEED A RELIABLE SOURCE !!) - inner_diameter_center_feed = D['Center feed diameter'] - outer_diameter_center_feed = inner_diameter_center_feed + 2*thickness_metal_wall - volume_center_feed = (np.pi*D['Center feed depth']/4)*(outer_diameter_center_feed**2 - inner_diameter_center_feed **2) - density_ss = 7930 # kg/m3, 18/8 Chromium - D['Stainless steel'] = volume_center_feed*density_ss # in kg + # # Amount of concrete required + # D_tank = D['Clarifier depth']*39.37 # m to inches + # # Thickness of the wall concrete, [m]. Default to be minimum of 1 ft with 1 in added for every ft of depth over 12 ft. (Brian's code) + # thickness_concrete_wall = (1 + max(D_tank-12, 0)/12)*0.3048 # from feet to m + # inner_diameter = D['Clarifier diameter'] + # outer_diameter = inner_diameter + 2*thickness_concrete_wall + # D['Volume of concrete wall'] = (np.pi*D['Clarifier depth']/4)*(outer_diameter**2 - inner_diameter**2) + + # # Concrete slab thickness, [ft], default to be 2 in thicker than the wall thickness. (Brian's code) + # thickness_concrete_slab = thickness_concrete_wall + (2/12)*0.3048 # from inch to m + # # From Brian's code + # D['Volume of concrete slab'] = (thickness_concrete_slab + thickness_concrete_wall)*D['Surface area'] + + # # Amount of metal required for center feed + # thickness_metal_wall = 0.3048 # equal to 1 feet, in m (!! NEED A RELIABLE SOURCE !!) + # inner_diameter_center_feed = D['Center feed diameter'] + # outer_diameter_center_feed = inner_diameter_center_feed + 2*thickness_metal_wall + # volume_center_feed = (np.pi*D['Center feed depth']/4)*(outer_diameter_center_feed**2 - inner_diameter_center_feed **2) + # density_ss = 7930 # kg/m3, 18/8 Chromium + # D['Stainless steel'] = volume_center_feed*density_ss # in kg - # Pumps - pipe, pumps = self._design_pump() - D['Pump pipe stainless steel'] = pipe - D['Pump stainless steel'] = pumps + # # Pumps + # pipe, pumps = self._design_pump() + # D['Pump pipe stainless steel'] = pipe + # D['Pump stainless steel'] = pumps - # For secondary clarifier - D['Number of pumps'] = 2*D['Number of clarifiers'] + # # For secondary clarifier + # D['Number of pumps'] = 2*D['Number of clarifiers'] - def _cost(self): + # def _cost(self): - D = self.design_results - C = self.baseline_purchase_costs + # D = self.design_results + # C = self.baseline_purchase_costs - # Construction of concrete and stainless steel walls - C['Wall concrete'] = D['Number of clarifiers']*D['Volume of concrete wall']*self.wall_concrete_unit_cost + # # Construction of concrete and stainless steel walls + # C['Wall concrete'] = D['Number of clarifiers']*D['Volume of concrete wall']*self.wall_concrete_unit_cost - C['Slab concrete'] = D['Number of clarifiers']*D['Volume of concrete slab']*self.slab_concrete_unit_cost + # C['Slab concrete'] = D['Number of clarifiers']*D['Volume of concrete slab']*self.slab_concrete_unit_cost - C['Wall stainless steel'] = D['Number of clarifiers']*D['Stainless steel']*self.stainless_steel_unit_cost + # C['Wall stainless steel'] = D['Number of clarifiers']*D['Stainless steel']*self.stainless_steel_unit_cost - # Cost of equipment + # # Cost of equipment - # Source of scaling exponents: Process Design and Economics for Biochemical Conversion of Lignocellulosic Biomass to Ethanol by NREL. + # # Source of scaling exponents: Process Design and Economics for Biochemical Conversion of Lignocellulosic Biomass to Ethanol by NREL. - # Scraper - # Source: https://www.alibaba.com/product-detail/Peripheral-driving-clarifier-mud-scraper-waste_1600891102019.html?spm=a2700.details.0.0.47ab45a4TP0DLb - # base_cost_scraper = 2500 - # base_flow_scraper = 1 # in m3/hr (!!! Need to know whether this is for solids or influent !!!) - clarifier_flow = D['Volumetric flow']/24 # in m3/hr - # C['Scraper'] = D['Number of clarifiers']*base_cost_scraper*(clarifier_flow/base_flow_scraper)**0.6 - # base_power_scraper = 2.75 # in kW - # THE EQUATION BELOW IS NOT CORRECT TO SCALE SCRAPER POWER REQUIREMENTS - # scraper_power = D['Number of clarifiers']*base_power_scraper*(clarifier_flow/base_flow_scraper)**0.6 + # # Scraper + # # Source: https://www.alibaba.com/product-detail/Peripheral-driving-clarifier-mud-scraper-waste_1600891102019.html?spm=a2700.details.0.0.47ab45a4TP0DLb + # # base_cost_scraper = 2500 + # # base_flow_scraper = 1 # in m3/hr (!!! Need to know whether this is for solids or influent !!!) + # clarifier_flow = D['Volumetric flow']/24 # in m3/hr + # # C['Scraper'] = D['Number of clarifiers']*base_cost_scraper*(clarifier_flow/base_flow_scraper)**0.6 + # # base_power_scraper = 2.75 # in kW + # # THE EQUATION BELOW IS NOT CORRECT TO SCALE SCRAPER POWER REQUIREMENTS + # # scraper_power = D['Number of clarifiers']*base_power_scraper*(clarifier_flow/base_flow_scraper)**0.6 - # v notch weir - # Source: https://www.alibaba.com/product-detail/50mm-Tube-Settler-Media-Modules-Inclined_1600835845218.html?spm=a2700.galleryofferlist.normal_offer.d_title.69135ff6o4kFPb - base_cost_v_notch_weir = 6888 - base_flow_v_notch_weir = 10 # in m3/hr - C['v notch weir'] = D['Number of clarifiers']*base_cost_v_notch_weir*(clarifier_flow/base_flow_v_notch_weir)**0.6 + # # v notch weir + # # Source: https://www.alibaba.com/product-detail/50mm-Tube-Settler-Media-Modules-Inclined_1600835845218.html?spm=a2700.galleryofferlist.normal_offer.d_title.69135ff6o4kFPb + # base_cost_v_notch_weir = 6888 + # base_flow_v_notch_weir = 10 # in m3/hr + # C['v notch weir'] = D['Number of clarifiers']*base_cost_v_notch_weir*(clarifier_flow/base_flow_v_notch_weir)**0.6 - # Pump (construction and maintainance) - pumps = self.pumps - add_OPEX = self.add_OPEX - pump_cost = 0. - building_cost = 0. - opex_o = 0. - opex_m = 0. + # # Pump (construction and maintainance) + # pumps = self.pumps + # add_OPEX = self.add_OPEX + # pump_cost = 0. + # building_cost = 0. + # opex_o = 0. + # opex_m = 0. - # i would be 0 and 1 for RAS and WAS respectively - for i in pumps: - p = getattr(self, f'{i}_pump') - p_cost = p.baseline_purchase_costs - p_add_opex = p.add_OPEX - pump_cost += p_cost['Pump'] - building_cost += p_cost['Pump building'] - opex_o += p_add_opex['Pump operating'] - opex_m += p_add_opex['Pump maintenance'] + # # i would be 0 and 1 for RAS and WAS respectively + # for i in pumps: + # p = getattr(self, f'{i}_pump') + # p_cost = p.baseline_purchase_costs + # p_add_opex = p.add_OPEX + # pump_cost += p_cost['Pump'] + # building_cost += p_cost['Pump building'] + # opex_o += p_add_opex['Pump operating'] + # opex_m += p_add_opex['Pump maintenance'] - # All costs associated with pumping need to be multiplied by number of clarifiers - C['Pumps'] = pump_cost*D['Number of clarifiers'] - C['Pump building'] = building_cost*D['Number of clarifiers'] - add_OPEX['Pump operating'] = opex_o*D['Number of clarifiers'] - add_OPEX['Pump maintenance'] = opex_m*D['Number of clarifiers'] + # # All costs associated with pumping need to be multiplied by number of clarifiers + # C['Pumps'] = pump_cost*D['Number of clarifiers'] + # C['Pump building'] = building_cost*D['Number of clarifiers'] + # add_OPEX['Pump operating'] = opex_o*D['Number of clarifiers'] + # add_OPEX['Pump maintenance'] = opex_m*D['Number of clarifiers'] - # Power - pumping = 0. - for ID in self.pumps: - p = getattr(self, f'{ID}_pump') - if p is None: - continue - pumping += p.power_utility.rate + # # Power + # pumping = 0. + # for ID in self.pumps: + # p = getattr(self, f'{ID}_pump') + # if p is None: + # continue + # pumping += p.power_utility.rate - pumping = pumping*D['Number of clarifiers'] + # pumping = pumping*D['Number of clarifiers'] - self.power_utility.rate += pumping - # self.power_utility.consumption += scraper_power + # self.power_utility.rate += pumping + # # self.power_utility.consumption += scraper_power # %% class IdealClarifier(SanUnit): """ Ideal clarifier that settles suspended solids by specified efficiency. Has - no design or costing algorithm. + no design or costing algorithm. Governing equations are + + .. math:: + Q_i X_i = Q_e X_e + Q_s X_s + + Q_i = Q_e + Q_s + + X_e = X_i * (1-e_rmv) + + where subscripts 'i', 'e', 's' represent influent, overflow effluent, and + underflow sludge, respectively. 'Q' indicates volumetric flowrate and 'X' + indicates suspended solids concentration. Parameters ---------- sludge_flow_rate : float, optional Underflow sludge flowrate [m3/d]. The default is 2000. solids_removal_efficiency : float, optional - Removal efficiency of suspended solids, unitless. The default is 0.995. + Removal efficiency (concentration basis) of suspended solids, unitless. + The default is 0.995. sludge_MLSS : float, optional Underflow MLSS [mg/L]. Used only when either `solids_removal_efficiency` or `sludge_flow_rate` is unspecified. The default is None. @@ -862,100 +874,108 @@ def sludge_MLSS(self, MLSS): f'one of them is unspecified.') self._MLSS = MLSS - - def _calc_Qs(self, TSS_in=None, Q_in=None): - if Q_in is None: Q_in = self._mixed.get_total_flow('m3/d') - if TSS_in is None: TSS_in = self._mixed.get_TSS() - return Q_in*TSS_in*self._e_rmv/(self._MLSS-TSS_in) - - def _calc_ermv(self, TSS_in=None, Q_in=None): - if Q_in is None: Q_in = self._mixed.get_total_flow('m3/d') - if TSS_in is None: TSS_in = self._mixed.get_TSS() - return self._Qs*(self._MLSS-TSS_in)/TSS_in/(Q_in-self._Qs) - - def _calc_SS(self, SS_in=None, Q_in=None): - if Q_in is None: Q_in = self._mixed.get_total_flow('m3/d') - if SS_in is None: SS_in = self._mixed.get_TSS() - SS_e = (1-self._e_rmv)*SS_in - Qs = self._Qs - Qe = Q_in - Qs - return SS_e, (Q_in*SS_in - Qe*SS_e)/Qs + # def _calc_Qs(self, TSS_in=None, Q_in=None): + # if Q_in is None: Q_in = self._mixed.get_total_flow('m3/d') + # if TSS_in is None: TSS_in = self._mixed.get_TSS() + # # return Q_in*TSS_in*self._e_rmv/(self._MLSS-TSS_in) + # er = self._e_rmv + # return er * TSS_in/(self._MLSS - (1-er)*TSS_in) * Q_in + + # def _calc_ermv(self, TSS_in=None, Q_in=None): + # if Q_in is None: Q_in = self._mixed.get_total_flow('m3/d') + # if TSS_in is None: TSS_in = self._mixed.get_TSS() + # # return self._Qs*(self._MLSS-TSS_in)/TSS_in/(Q_in-self._Qs) + # Q_e = Q_in - self._Qs + # TSS_e = (Q_in * TSS_in - self._Qs * self._MLSS) / Q_e + # return 1 - TSS_e / TSS_in + + # def _calc_SS(self, SS_in=None, Q_in=None): + # if Q_in is None: Q_in = self._mixed.get_total_flow('m3/d') + # if SS_in is None: SS_in = self._mixed.get_TSS() + # SS_e = (1-self._e_rmv)*SS_in + # Qs = self._Qs + # Qe = Q_in - Qs + # return SS_e, (Q_in*SS_in - Qe*SS_e)/Qs def _run(self): inf = self._mixed inf.mix_from(self.ins) of, uf = self.outs - Qs, e_rmv = self._Qs, self._e_rmv TSS_in = inf.get_TSS() - Q_in = inf.F_vol * 24 # m3/d - if Qs is None: Qs = self._calc_Qs(TSS_in, Q_in) - if e_rmv is None: e_rmv = self._calc_ermv(TSS_in, Q_in) - f_Qu = Qs/Q_in - x = inf.components.x - split_to_uf = (1-x)*f_Qu + x*e_rmv - if any(split_to_uf > 1): split_to_uf = 1 - inf.split_to(uf, of, split_to_uf) + if TSS_in <= 0: + uf.empty() + of.copy_like(inf) + else: + Q_in = inf.F_vol * 24 # m3/d + x = inf.components.x + Qs, e_rmv, mlss = self._Qs, self._e_rmv, self._MLSS + if Qs and e_rmv: + f_Qu = Qs/Q_in + f_Xu = e_rmv + (1-e_rmv) * f_Qu + elif Qs and mlss: + f_Qu = Qs/Q_in + f_Xu = f_Qu*mlss/TSS_in + elif e_rmv and mlss: + f_Qu = e_rmv / (mlss/TSS_in - (1-e_rmv)) + f_Xu = e_rmv + (1-e_rmv) * f_Qu + split_to_uf = (1-x)*f_Qu + x*f_Xu + if any(split_to_uf > 1): split_to_uf = 1 + inf.split_to(uf, of, split_to_uf) def _init_state(self): inf = self._mixed C_in = inf.conc - TSS_in = inf.get_TSS() Q_in = inf.F_vol * 24 self._state = np.append(C_in, Q_in) self._dstate = self._state * 0. - if not self._Qs: self._Qs = self._calc_Qs(TSS_in, Q_in) - if not self._e_rmv: self._e_rmv = self._calc_ermv(TSS_in, Q_in) def _update_state(self): arr = self._state Cs = arr[:-1] Qi = arr[-1] - Qs = self._Qs - e_rmv = self._e_rmv + Qs, e_rmv, mlss = self._Qs, self._e_rmv, self._MLSS x = self.components.x + i_tss = x * self.components.i_mass of, uf = self.outs if uf.state is None: uf.state = np.zeros(len(x)+1) if of.state is None: of.state = np.zeros(len(x)+1) + if Qs: + Qe = Qi - Qs + if e_rmv: + fuf = e_rmv * Qi/Qs + (1-e_rmv) + fof = 1-e_rmv + elif mlss: + tss_in = sum(Cs * i_tss) + tss_e = (Qi * tss_in - Qs * mlss)/Qe + fuf = mlss/tss_in + fof = tss_e/tss_in + elif e_rmv and mlss: + tss_in = sum(Cs * i_tss) + Qs = Qi * e_rmv / (mlss/tss_in - (1-e_rmv)) + Qe = Qi - Qs + fuf = mlss/tss_in + fof = 1-e_rmv + else: + raise RuntimeError('missing parameter') + if Qs >= Qi: uf.state[:] = arr of.state[:] = 0. - elif Qs <= 0: - uf.state[:] = 0. - of.state[:] = arr else: - self._f_uf = fuf = e_rmv*Qi/Qs - self._f_of = fof = (1-e_rmv)/(1-Qs/Qi) + self._f_uf = fuf + self._f_of = fof uf.state[:-1] = Cs * ((1-x) + x*fuf) uf.state[-1] = Qs of.state[:-1] = Cs * ((1-x) + x*fof) - of.state[-1] = Qi - Qs + of.state[-1] = Qe def _update_dstate(self): - arr = self._dstate - dCs = arr[:-1] - dQi = arr[-1] - Cs = self._state[:-1] - Qi = self._state[-1] - Qs = self._Qs - e_rmv = self._e_rmv - x = self.components.x - of, uf = self.outs + x = self.components.x if uf.dstate is None: uf.dstate = np.zeros(len(x)+1) if of.dstate is None: of.dstate = np.zeros(len(x)+1) - if Qs >= Qi: - uf.dstate[:] = arr - of.dstate[:] = 0. - elif Qs <= 0: - uf.dstate[:] = 0. - of.dstate[:] = arr - else: - uf.dstate[:-1] = dCs * ((1-x) + x*self._f_uf) + Cs*x*e_rmv*dQi/Qs - uf.dstate[-1] = 0. - of.dstate[:-1] = dCs * ((1-x) + x*self._f_of) - Cs*x*(1-e_rmv)*Qs/(Qi-Qs)**2 * dQi - of.dstate[-1] = dQi @property def AE(self): @@ -965,24 +985,24 @@ def AE(self): def _compile_AE(self): _state = self._state - _dstate = self._dstate + # _dstate = self._dstate _update_state = self._update_state - _update_dstate = self._update_dstate + # _update_dstate = self._update_dstate def yt(t, QC_ins, dQC_ins): Q_ins = QC_ins[:, -1] C_ins = QC_ins[:, :-1] - dQ_ins = dQC_ins[:, -1] - dC_ins = dQC_ins[:, :-1] + # dQ_ins = dQC_ins[:, -1] + # dC_ins = dQC_ins[:, :-1] Q = Q_ins.sum() C = Q_ins @ C_ins / Q _state[-1] = Q _state[:-1] = C - Q_dot = dQ_ins.sum() - C_dot = (dQ_ins @ C_ins + Q_ins @ dC_ins - Q_dot * C)/Q - _dstate[-1] = Q_dot - _dstate[:-1] = C_dot + # Q_dot = dQ_ins.sum() + # C_dot = (dQ_ins @ C_ins + Q_ins @ dC_ins - Q_dot * C)/Q + # _dstate[-1] = Q_dot + # _dstate[:-1] = C_dot _update_state() - _update_dstate() + # _update_dstate() self._AE = yt @@ -1000,7 +1020,7 @@ def calc_f_i(fx, f_corr, HRT): class PrimaryClarifierBSM2(SanUnit): """ - A Primary clarifier based on BSM2 Layout. [1] + A Primary clarifier based on the Otterpohl model [1] in BSM2 [2]. Parameters ---------- @@ -1014,19 +1034,12 @@ class PrimaryClarifierBSM2(SanUnit): Clarifier volume, in m^3. The default is 900. ratio_uf : float The volumetric ratio of sludge to primary influent. The default is 0.007, - based on IWA report.[1] + based on IWA report.[2] mean_f_x : float, optional The average fraction of particulate COD out of total COD in primary influent. The default is 0.85. f_corr : float - Dimensionless correction factor for removal efficiency in the primary clarifier.[1] - cylindrical_depth : float, optional - The depth of the cylindrical portion of clarifier [in m]. - upflow_velocity : float, optional - Speed with which influent enters the center feed of the clarifier [m/hr]. - The default is 43.2. - F_BM : dict - Equipment bare modules. + Dimensionless correction factor for removal efficiency in the primary clarifier.[2] Examples -------- @@ -1098,33 +1111,24 @@ class PrimaryClarifierBSM2(SanUnit): References ---------- - [1] Gernaey, Krist V., Ulf Jeppsson, Peter A. Vanrolleghem, and John B. Copp. - Benchmarking of control strategies for wastewater treatment plants. IWA publishing, 2014. - [2] Metcalf, Leonard, Harrison P. Eddy, and Georg Tchobanoglous. Wastewater - engineering: treatment, disposal, and reuse. Vol. 4. New York: McGraw-Hill, 1991. - [3] Otterpohl R. and Freund M. (1992). Dynamic Models for clarifiers of activated sludge + [1] Otterpohl R. and Freund M. (1992). Dynamic Models for clarifiers of activated sludge plants with dry and wet weather flows. Water Sci. Technol., 26(5-6), 1391-1400. + [2] Gernaey, Krist V., Ulf Jeppsson, Peter A. Vanrolleghem, and John B. Copp. + Benchmarking of control strategies for wastewater treatment plants. IWA publishing, 2014. """ _N_ins = 3 _N_outs = 2 # [0] effluent; [1] underflow _ins_size_is_fixed = False - # Costs - wall_concrete_unit_cost = 650 / 0.765 # $/m3, 0.765 is to convert from $/yd3 - stainless_steel_unit_cost=1.8 # $/kg (Taken from Joy's METAB code) https://www.alibaba.com/product-detail/brushed-stainless-steel-plate-304l-stainless_1600391656401.html?spm=a2700.details.0.0.230e67e6IKwwFd - - pumps = ('sludge',) t_m = 0.125 # Smoothing time constant for qm calculation def __init__(self, ID='', ins=None, outs=(), thermo=None, isdynamic=True, init_with='WasteStream', - volume=900, ratio_uf=0.007, mean_f_x=0.85, - f_corr=0.65, cylindrical_depth = 5, upflow_velocity=43.2, + volume=900, ratio_uf=0.007, mean_f_x=0.85, f_corr=0.65, F_BM=default_F_BM, **kwargs): SanUnit.__init__(self, ID, ins, outs, thermo, isdynamic=isdynamic, init_with=init_with) - # self.HRT = HRT # in days self._mixed = self.ins[0].copy(f'{ID}_mixed') self._sludge = np.ones(len(self.components)+1) self._effluent = np.ones(len(self.components)+1) @@ -1132,8 +1136,6 @@ def __init__(self, ID='', ins=None, outs=(), thermo=None, self.ratio_uf = ratio_uf self.f_x = mean_f_x self.f_corr = f_corr - self.cylindrical_depth = cylindrical_depth # in m - self.upflow_velocity = upflow_velocity # in m/hr (converted from 12 mm/sec) self.F_BM.update(default_F_BM) self._concs = None @@ -1266,17 +1268,17 @@ class PrimaryClarifier(IdealClarifier): >>> cmps_test = cmps.subgroup(['S_F', 'S_NH4', 'X_OHO', 'H2O']) >>> set_thermo(cmps_test) >>> ws = WasteStream('ws', S_F = 10, S_NH4 = 20, X_OHO = 15, H2O=1000) - >>> from qsdsan.sanunits import PrimaryClarifier, CSTR - >>> M1 = CSTR('mixer', ins=ws, outs='out', aeration=None) - >>> PC = PrimaryClarifier(ID='IC', ins=M1-0, outs=('effluent', 'sludge'), + >>> from qsdsan.sanunits import PrimaryClarifier + >>> PC = PrimaryClarifier(ID='PC', ins=ws, outs=('effluent', 'sludge'), ... solids_removal_efficiency=0.6, - ... sludge_flow_rate=ws.F_vol*24*0.3) - >>> sys = System('sys', path=(M1, PC)) + ... sludge_flow_rate=ws.F_vol*24*0.3, + ... isdynamic=True) + >>> sys = System('sys', path=(PC,)) >>> sys.simulate(t_span=(0,10), method='BDF') # doctest: +ELLIPSIS >>> PC.show() # doctest: +ELLIPSIS - PrimaryClarifier: IC + PrimaryClarifier: PC ins... - [0] out from CSTR-mixer + [0] ws phase: 'l', T: 298.15 K, P: 101325 Pa flow (g/hr): S_F 1e+04 S_NH4 2e+04 @@ -1298,34 +1300,34 @@ class PrimaryClarifier(IdealClarifier): phase: 'l', T: 298.15 K, P: 101325 Pa flow (g/hr): S_F 7e+03 S_NH4 1.4e+04 - X_OHO 6e+03 - H2O 7.03e+05 + X_OHO 4.2e+03 + H2O 7.04e+05 WasteStream-specific properties: pH : 7.0 - COD : 17734.2 mg/L - BOD : 11484.7 mg/L - TC : 6051.5 mg/L - TOC : 6051.5 mg/L - TN : 19943.9 mg/L - TP : 251.0 mg/L - TK : 39.0 mg/L - TSS : 6356.8 mg/L + COD : 15278.7 mg/L + BOD : 10093.4 mg/L + TC : 5152.8 mg/L + TOC : 5152.8 mg/L + TN : 19776.2 mg/L + TP : 204.4 mg/L + TK : 27.3 mg/L + TSS : 4449.8 mg/L [1] sludge phase: 'l', T: 298.15 K, P: 101325 Pa flow (g/hr): S_F 3e+03 S_NH4 6e+03 - X_OHO 9e+03 - H2O 2.97e+05 + X_OHO 1.08e+04 + H2O 2.96e+05 WasteStream-specific properties: pH : 7.0 - COD : 38196.8 mg/L - BOD : 23079.7 mg/L - TC : 13540.8 mg/L - TOC : 13540.8 mg/L - TN : 21341.5 mg/L - TP : 639.8 mg/L - TK : 136.6 mg/L - TSS : 22248.9 mg/L + COD : 43926.4 mg/L + BOD : 26326.2 mg/L + TC : 15637.8 mg/L + TOC : 15637.8 mg/L + TN : 21732.9 mg/L + TP : 748.7 mg/L + TK : 163.9 mg/L + TSS : 26698.6 mg/L References ---------- @@ -1341,12 +1343,12 @@ class PrimaryClarifier(IdealClarifier): _ins_size_is_fixed = False _outs_size_is_fixed = True - # Costs - wall_concrete_unit_cost = 1081.73 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) - slab_concrete_unit_cost = 582.48 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) - stainless_steel_unit_cost=1.8 # Alibaba. Brushed Stainless Steel Plate 304. https://www.alibaba.com/product-detail/brushed-stainless-steel-plate-304l-stainless_1600391656401.html?spm=a2700.details.0.0.230e67e6IKwwFd + # # Costs + # wall_concrete_unit_cost = 1081.73 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) + # slab_concrete_unit_cost = 582.48 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) + # stainless_steel_unit_cost=1.8 # Alibaba. Brushed Stainless Steel Plate 304. https://www.alibaba.com/product-detail/brushed-stainless-steel-plate-304l-stainless_1600391656401.html?spm=a2700.details.0.0.230e67e6IKwwFd - pumps = ('sludge',) + # pumps = ('sludge',) def __init__(self, ID='', ins=None, outs=(), sludge_flow_rate=2000, solids_removal_efficiency=0.6, @@ -1356,7 +1358,6 @@ def __init__(self, ID='', ins=None, outs=(), super().__init__(ID, ins, outs, thermo, sludge_flow_rate=sludge_flow_rate, solids_removal_efficiency=solids_removal_efficiency, - # thermo=thermo, isdynamic=isdynamic, init_with=init_with) @@ -1378,241 +1379,241 @@ def __init__(self, ID='', ins=None, outs=(), # else: # raise ValueError('solids_loading_rate of the clarifier expected from user') - def _design_pump(self): - ID, pumps = self.ID, self.pumps - sludge = self._sludge - sludge.copy_like(self.outs[1]) + # def _design_pump(self): + # ID, pumps = self.ID, self.pumps + # sludge = self._sludge + # sludge.copy_like(self.outs[1]) - ins_dct = { - 'sludge': sludge, - } + # ins_dct = { + # 'sludge': sludge, + # } - type_dct = dict.fromkeys(pumps, 'sludge') - inputs_dct = dict.fromkeys(pumps, (1,)) + # type_dct = dict.fromkeys(pumps, 'sludge') + # inputs_dct = dict.fromkeys(pumps, (1,)) - D = self.design_results - influent_Q = sludge.get_total_flow('m3/hr')/D['Number of clarifiers'] - influent_Q_mgd = influent_Q*0.00634 # m3/hr to MGD + # D = self.design_results + # influent_Q = sludge.get_total_flow('m3/hr')/D['Number of clarifiers'] + # influent_Q_mgd = influent_Q*0.00634 # m3/hr to MGD - for i in pumps: - if hasattr(self, f'{i}_pump'): - p = getattr(self, f'{i}_pump') - setattr(p, 'add_inputs', inputs_dct[i]) - else: - ID = f'{ID}_{i}' - capacity_factor=1 - pump = WWTpump( - ID=ID, ins= ins_dct[i], thermo = self.thermo, pump_type=type_dct[i], - Q_mgd=influent_Q_mgd, add_inputs=inputs_dct[i], - capacity_factor=capacity_factor, - include_pump_cost=True, - include_building_cost=False, - include_OM_cost=True, - ) + # for i in pumps: + # if hasattr(self, f'{i}_pump'): + # p = getattr(self, f'{i}_pump') + # setattr(p, 'add_inputs', inputs_dct[i]) + # else: + # ID = f'{ID}_{i}' + # capacity_factor=1 + # pump = WWTpump( + # ID=ID, ins= ins_dct[i], thermo = self.thermo, pump_type=type_dct[i], + # Q_mgd=influent_Q_mgd, add_inputs=inputs_dct[i], + # capacity_factor=capacity_factor, + # include_pump_cost=True, + # include_building_cost=False, + # include_OM_cost=True, + # ) - setattr(self, f'{i}_pump', pump) - - pipe_ss, pump_ss = 0., 0. - for i in pumps: - p = getattr(self, f'{i}_pump') - p.simulate() - p_design = p.design_results - pipe_ss += p_design['Pump pipe stainless steel'] - pump_ss += p_design['Pump stainless steel'] - return pipe_ss, pump_ss + # setattr(self, f'{i}_pump', pump) + + # pipe_ss, pump_ss = 0., 0. + # for i in pumps: + # p = getattr(self, f'{i}_pump') + # p.simulate() + # p_design = p.design_results + # pipe_ss += p_design['Pump pipe stainless steel'] + # pump_ss += p_design['Pump stainless steel'] + # return pipe_ss, pump_ss - _units = { - 'Number of clarifiers': 'ea', - 'SOR': 'm3/day/m2', - 'Volumetric flow': 'm3/day', - 'Surface area': 'm2', - 'Cylindrical diameter': 'm', - 'Conical radius': 'm', - 'Conical depth': 'm', - 'Clarifier depth': 'm', - 'Cylindrical depth': 'm', - 'Cylindrical volume': 'm3', - 'Conical volume': 'm3', - 'Volume': 'm3', - 'Hydraulic Retention Time': 'hr', - 'Center feed depth': 'm', - 'Downward flow velocity': 'm/hr', - 'Center feed diameter': 'm', - 'Volume of concrete wall': 'm3', - 'Volume of concrete slab': 'm3', - 'Stainless steel': 'kg', - 'Pump pipe stainless steel' : 'kg', - 'Pump stainless steel': 'kg', - 'Number of pumps': 'ea' - } + # _units = { + # 'Number of clarifiers': 'ea', + # 'SOR': 'm3/day/m2', + # 'Volumetric flow': 'm3/day', + # 'Surface area': 'm2', + # 'Cylindrical diameter': 'm', + # 'Conical radius': 'm', + # 'Conical depth': 'm', + # 'Clarifier depth': 'm', + # 'Cylindrical depth': 'm', + # 'Cylindrical volume': 'm3', + # 'Conical volume': 'm3', + # 'Volume': 'm3', + # 'Hydraulic Retention Time': 'hr', + # 'Center feed depth': 'm', + # 'Downward flow velocity': 'm/hr', + # 'Center feed diameter': 'm', + # 'Volume of concrete wall': 'm3', + # 'Volume of concrete slab': 'm3', + # 'Stainless steel': 'kg', + # 'Pump pipe stainless steel' : 'kg', + # 'Pump stainless steel': 'kg', + # 'Number of pumps': 'ea' + # } - def _design(self): - - mixed = self._mixed - mixed.mix_from(self.ins) - D = self.design_results - - # Number of clarifiers based on tentative suggestions by Jeremy - # (would be verified through collaboration with industry) - total_flow = (mixed.get_total_flow('m3/hr')*24)/3785 # in MGD - if total_flow <= 3: - D['Number of clarifiers'] = 2 - elif total_flow > 3 and total_flow <= 8: - D['Number of clarifiers'] = 3 - elif total_flow > 8 and total_flow <=20: - D['Number of clarifiers'] = 4 - else: - D['Number of clarifiers'] = 4 - total_flow -= 20 - D['Number of clarifiers'] += np.ceil(total_flow/20) + # def _design(self): + + # mixed = self._mixed + # mixed.mix_from(self.ins) + # D = self.design_results + + # # Number of clarifiers based on tentative suggestions by Jeremy + # # (would be verified through collaboration with industry) + # total_flow = (mixed.get_total_flow('m3/hr')*24)/3785 # in MGD + # if total_flow <= 3: + # D['Number of clarifiers'] = 2 + # elif total_flow > 3 and total_flow <= 8: + # D['Number of clarifiers'] = 3 + # elif total_flow > 8 and total_flow <=20: + # D['Number of clarifiers'] = 4 + # else: + # D['Number of clarifiers'] = 4 + # total_flow -= 20 + # D['Number of clarifiers'] += np.ceil(total_flow/20) - D['SOR'] = self.surface_overflow_rate # in (m3/day)/m2 - D['Volumetric flow'] = (mixed.get_total_flow('m3/hr')*24)/D['Number of clarifiers'] # m3/day - D['Surface area'] = D['Volumetric flow']/D['SOR'] # in m2 - D['Cylindrical diameter'] = np.sqrt(4*D['Surface area']/np.pi) #in m - - #Check on cylindrical diameter [2, 3] - if D['Cylindrical diameter'] < 3 or D['Cylindrical diameter'] > 60: - Cylindrical_dia = D['Cylindrical diameter'] - warn(f'Cylindrical diameter = {Cylindrical_dia} is not between 3 m and 60 m') - - D['Conical radius'] = D['Cylindrical diameter']/2 - # The slope of the bottom conical floor lies between 1:10 to 1:12 [3, 4] - D['Conical depth'] = D['Conical radius']/12 - D['Clarifier depth'] = self.depth_clarifier #in m - D['Cylindrical depth'] = D['Clarifier depth'] - D['Conical depth'] - - # Check on cylindrical and conical depths - if D['Cylindrical depth'] < D['Conical depth']: - Cylindrical_depth = D['Cylindrical depth'] - Conical_depth = D['Conical depth'] - warn(f'Cylindrical depth = {Cylindrical_depth} is lower than Conical depth = {Conical_depth}') - - D['Cylindrical volume'] = np.pi*np.square(D['Cylindrical diameter']/2)*D['Cylindrical depth'] #in m3 - D['Conical volume'] = (3.14/3)*(D['Conical radius']**2)*D['Conical depth'] #in m3 - D['Volume'] = D['Cylindrical volume'] + D['Conical volume'] #in m3 - - D['Hydraulic Retention Time'] = D['Volume']/(D['Volumetric flow']/24) #in hrs - - # Check on cylinderical HRT [3] - if D['Hydraulic Retention Time'] < 1.5 or D['Hydraulic Retention Time'] > 2.5: - HRT = D['Hydraulic Retention Time'] - warn(f'HRT = {HRT} is not between 1.5 and 2.5 hrs') - - # The design here is for center feed of clarifier. - - # Depth of the center feed lies between 30-75% of sidewater depth. [3, 4] - D['Center feed depth'] = 0.5*D['Cylindrical depth'] - # Typical conventional feed wells are designed for an average downflow velocity - # of 10-13 mm/s and maximum velocity of 25-30 mm/s. [4] - peak_flow_safety_factor = 2.5 # assumed based on average and maximum velocities - D['Downward flow velocity'] = self.downward_flow_velocity*peak_flow_safety_factor # in m/hr - - Center_feed_area = (D['Volumetric flow']/24)/D['Downward flow velocity'] # in m2 - - D['Center feed diameter'] = np.sqrt(4*Center_feed_area/np.pi) - - #Sanity check: Diameter of the center feed lies between 15-25% of tank diameter [4] - #The lower limit of this check has been modified to 10% based on advised range of down flow velocity in [4]. - if D['Center feed diameter'] < 0.10*D['Cylindrical diameter'] or D['Center feed diameter'] > 0.25*D['Cylindrical diameter']: - cf_dia = D['Center feed diameter'] - tank_dia = D['Cylindrical diameter'] - warn(f'Diameter of the center feed does not lie between 15-25% of tank diameter. It is {cf_dia*100/tank_dia}% of tank diameter') - - # Amount of concrete required - D_tank = D['Cylindrical depth']*39.37 # m to inches - # Thickness of the wall concrete [m]. Default to be minimum of 1 feet with 1 inch added for every feet of depth over 12 feet. - thickness_concrete_wall = (1 + max(D_tank-12, 0)/12)*0.3048 # from feet to m - inner_diameter = D['Cylindrical diameter'] - outer_diameter = inner_diameter + 2*thickness_concrete_wall - volume_cylindercal_wall = (np.pi*D['Cylindrical depth']/4)*(outer_diameter**2 - inner_diameter**2) - D['Volume of concrete wall'] = volume_cylindercal_wall # in m3 - - # Concrete slab thickness, [ft], default to be 2 in thicker than the wall thickness. (Brian's code) - thickness_concrete_slab = thickness_concrete_wall + (2/12)*0.3048 # from inch to m - outer_diameter_cone = inner_diameter + 2*(thickness_concrete_wall + thickness_concrete_slab) - volume_conical_wall = (np.pi/(3*4))*(((D['Conical depth'] + thickness_concrete_wall + thickness_concrete_slab)*(outer_diameter_cone**2)) - (D['Conical depth']*(inner_diameter)**2)) - D['Volume of concrete slab'] = volume_conical_wall - - # Amount of metal required for center feed - thickness_metal_wall = 0.3048 # equal to 1 feet, in m (!! NEED A RELIABLE SOURCE !!) - inner_diameter_center_feed = D['Center feed diameter'] - outer_diameter_center_feed = inner_diameter_center_feed + 2*thickness_metal_wall - volume_center_feed = (3.14*D['Center feed depth']/4)*(outer_diameter_center_feed**2 - inner_diameter_center_feed**2) - density_ss = 7930 # kg/m3, 18/8 Chromium - D['Stainless steel'] = volume_center_feed*density_ss # in kg + # D['SOR'] = self.surface_overflow_rate # in (m3/day)/m2 + # D['Volumetric flow'] = (mixed.get_total_flow('m3/hr')*24)/D['Number of clarifiers'] # m3/day + # D['Surface area'] = D['Volumetric flow']/D['SOR'] # in m2 + # D['Cylindrical diameter'] = np.sqrt(4*D['Surface area']/np.pi) #in m + + # #Check on cylindrical diameter [2, 3] + # if D['Cylindrical diameter'] < 3 or D['Cylindrical diameter'] > 60: + # Cylindrical_dia = D['Cylindrical diameter'] + # warn(f'Cylindrical diameter = {Cylindrical_dia} is not between 3 m and 60 m') + + # D['Conical radius'] = D['Cylindrical diameter']/2 + # # The slope of the bottom conical floor lies between 1:10 to 1:12 [3, 4] + # D['Conical depth'] = D['Conical radius']/12 + # D['Clarifier depth'] = self.depth_clarifier #in m + # D['Cylindrical depth'] = D['Clarifier depth'] - D['Conical depth'] + + # # Check on cylindrical and conical depths + # if D['Cylindrical depth'] < D['Conical depth']: + # Cylindrical_depth = D['Cylindrical depth'] + # Conical_depth = D['Conical depth'] + # warn(f'Cylindrical depth = {Cylindrical_depth} is lower than Conical depth = {Conical_depth}') + + # D['Cylindrical volume'] = np.pi*np.square(D['Cylindrical diameter']/2)*D['Cylindrical depth'] #in m3 + # D['Conical volume'] = (3.14/3)*(D['Conical radius']**2)*D['Conical depth'] #in m3 + # D['Volume'] = D['Cylindrical volume'] + D['Conical volume'] #in m3 + + # D['Hydraulic Retention Time'] = D['Volume']/(D['Volumetric flow']/24) #in hrs + + # # Check on cylinderical HRT [3] + # if D['Hydraulic Retention Time'] < 1.5 or D['Hydraulic Retention Time'] > 2.5: + # HRT = D['Hydraulic Retention Time'] + # warn(f'HRT = {HRT} is not between 1.5 and 2.5 hrs') + + # # The design here is for center feed of clarifier. + + # # Depth of the center feed lies between 30-75% of sidewater depth. [3, 4] + # D['Center feed depth'] = 0.5*D['Cylindrical depth'] + # # Typical conventional feed wells are designed for an average downflow velocity + # # of 10-13 mm/s and maximum velocity of 25-30 mm/s. [4] + # peak_flow_safety_factor = 2.5 # assumed based on average and maximum velocities + # D['Downward flow velocity'] = self.downward_flow_velocity*peak_flow_safety_factor # in m/hr + + # Center_feed_area = (D['Volumetric flow']/24)/D['Downward flow velocity'] # in m2 + + # D['Center feed diameter'] = np.sqrt(4*Center_feed_area/np.pi) + + # #Sanity check: Diameter of the center feed lies between 15-25% of tank diameter [4] + # #The lower limit of this check has been modified to 10% based on advised range of down flow velocity in [4]. + # if D['Center feed diameter'] < 0.10*D['Cylindrical diameter'] or D['Center feed diameter'] > 0.25*D['Cylindrical diameter']: + # cf_dia = D['Center feed diameter'] + # tank_dia = D['Cylindrical diameter'] + # warn(f'Diameter of the center feed does not lie between 15-25% of tank diameter. It is {cf_dia*100/tank_dia}% of tank diameter') + + # # Amount of concrete required + # D_tank = D['Cylindrical depth']*39.37 # m to inches + # # Thickness of the wall concrete [m]. Default to be minimum of 1 feet with 1 inch added for every feet of depth over 12 feet. + # thickness_concrete_wall = (1 + max(D_tank-12, 0)/12)*0.3048 # from feet to m + # inner_diameter = D['Cylindrical diameter'] + # outer_diameter = inner_diameter + 2*thickness_concrete_wall + # volume_cylindercal_wall = (np.pi*D['Cylindrical depth']/4)*(outer_diameter**2 - inner_diameter**2) + # D['Volume of concrete wall'] = volume_cylindercal_wall # in m3 + + # # Concrete slab thickness, [ft], default to be 2 in thicker than the wall thickness. (Brian's code) + # thickness_concrete_slab = thickness_concrete_wall + (2/12)*0.3048 # from inch to m + # outer_diameter_cone = inner_diameter + 2*(thickness_concrete_wall + thickness_concrete_slab) + # volume_conical_wall = (np.pi/(3*4))*(((D['Conical depth'] + thickness_concrete_wall + thickness_concrete_slab)*(outer_diameter_cone**2)) - (D['Conical depth']*(inner_diameter)**2)) + # D['Volume of concrete slab'] = volume_conical_wall + + # # Amount of metal required for center feed + # thickness_metal_wall = 0.3048 # equal to 1 feet, in m (!! NEED A RELIABLE SOURCE !!) + # inner_diameter_center_feed = D['Center feed diameter'] + # outer_diameter_center_feed = inner_diameter_center_feed + 2*thickness_metal_wall + # volume_center_feed = (3.14*D['Center feed depth']/4)*(outer_diameter_center_feed**2 - inner_diameter_center_feed**2) + # density_ss = 7930 # kg/m3, 18/8 Chromium + # D['Stainless steel'] = volume_center_feed*density_ss # in kg - # Pumps - pipe, pumps = self._design_pump() - D['Pump pipe stainless steel'] = pipe - D['Pump stainless steel'] = pumps + # # Pumps + # pipe, pumps = self._design_pump() + # D['Pump pipe stainless steel'] = pipe + # D['Pump stainless steel'] = pumps - #For primary clarifier - D['Number of pumps'] = D['Number of clarifiers'] + # #For primary clarifier + # D['Number of pumps'] = D['Number of clarifiers'] - def _cost(self): - D = self.design_results - C = self.baseline_purchase_costs + # def _cost(self): + # D = self.design_results + # C = self.baseline_purchase_costs - # Construction of concrete and stainless steel walls - C['Wall concrete'] = D['Number of clarifiers']*D['Volume of concrete wall']*self.wall_concrete_unit_cost + # # Construction of concrete and stainless steel walls + # C['Wall concrete'] = D['Number of clarifiers']*D['Volume of concrete wall']*self.wall_concrete_unit_cost - C['Slab concrete'] = D['Number of clarifiers']*D['Volume of concrete slab']*self.slab_concrete_unit_cost + # C['Slab concrete'] = D['Number of clarifiers']*D['Volume of concrete slab']*self.slab_concrete_unit_cost - C['Wall stainless steel'] = D['Number of clarifiers']*D['Stainless steel']*self.stainless_steel_unit_cost + # C['Wall stainless steel'] = D['Number of clarifiers']*D['Stainless steel']*self.stainless_steel_unit_cost - # Cost of equipment + # # Cost of equipment - # Source of scaling exponents: Process Design and Economics for Biochemical Conversion of Lignocellulosic Biomass to Ethanol by NREL. + # # Source of scaling exponents: Process Design and Economics for Biochemical Conversion of Lignocellulosic Biomass to Ethanol by NREL. - # Scraper - # Source: https://www.alibaba.com/product-detail/Peripheral-driving-clarifier-mud-scraper-waste_1600891102019.html?spm=a2700.details.0.0.47ab45a4TP0DLb - # base_cost_scraper = 2500 - # base_flow_scraper = 1 # in m3/hr (!!! Need to know whether this is for solids or influent !!!) - clarifier_flow = D['Volumetric flow']/24 + # # Scraper + # # Source: https://www.alibaba.com/product-detail/Peripheral-driving-clarifier-mud-scraper-waste_1600891102019.html?spm=a2700.details.0.0.47ab45a4TP0DLb + # # base_cost_scraper = 2500 + # # base_flow_scraper = 1 # in m3/hr (!!! Need to know whether this is for solids or influent !!!) + # clarifier_flow = D['Volumetric flow']/24 - # C['Scraper'] = D['Number of clarifiers']*base_cost_scraper*(clarifier_flow/base_flow_scraper)**0.6 + # # C['Scraper'] = D['Number of clarifiers']*base_cost_scraper*(clarifier_flow/base_flow_scraper)**0.6 - # base_power_scraper = 2.75 # in kW - # THE EQUATION BELOW IS NOT CORRECT TO SCALE SCRAPER POWER REQUIREMENTS - # scraper_power = D['Number of clarifiers']*base_power_scraper*(clarifier_flow/base_flow_scraper)**0.6 + # # base_power_scraper = 2.75 # in kW + # # THE EQUATION BELOW IS NOT CORRECT TO SCALE SCRAPER POWER REQUIREMENTS + # # scraper_power = D['Number of clarifiers']*base_power_scraper*(clarifier_flow/base_flow_scraper)**0.6 - # v notch weir - # Source: https://www.alibaba.com/product-detail/50mm-Tube-Settler-Media-Modules-Inclined_1600835845218.html?spm=a2700.galleryofferlist.normal_offer.d_title.69135ff6o4kFPb - base_cost_v_notch_weir = 6888 - base_flow_v_notch_weir = 10 # in m3/hr - C['v notch weir'] = D['Number of clarifiers']*base_cost_v_notch_weir*(clarifier_flow/base_flow_v_notch_weir)**0.6 + # # v notch weir + # # Source: https://www.alibaba.com/product-detail/50mm-Tube-Settler-Media-Modules-Inclined_1600835845218.html?spm=a2700.galleryofferlist.normal_offer.d_title.69135ff6o4kFPb + # base_cost_v_notch_weir = 6888 + # base_flow_v_notch_weir = 10 # in m3/hr + # C['v notch weir'] = D['Number of clarifiers']*base_cost_v_notch_weir*(clarifier_flow/base_flow_v_notch_weir)**0.6 - # Pump (construction and maintainance) - pumps = self.pumps - add_OPEX = self.add_OPEX - pump_cost = 0. - building_cost = 0. - opex_o = 0. - opex_m = 0. + # # Pump (construction and maintainance) + # pumps = self.pumps + # add_OPEX = self.add_OPEX + # pump_cost = 0. + # building_cost = 0. + # opex_o = 0. + # opex_m = 0. - for i in pumps: - p = getattr(self, f'{i}_pump') - p_cost = p.baseline_purchase_costs - p_add_opex = p.add_OPEX - pump_cost += p_cost['Pump'] - building_cost += p_cost['Pump building'] - opex_o += p_add_opex['Pump operating'] - opex_m += p_add_opex['Pump maintenance'] - - N = D['Number of clarifiers'] - C['Pumps'] = pump_cost*N - C['Pump building'] = building_cost*N - add_OPEX['Pump operating'] = opex_o*N - add_OPEX['Pump maintenance'] = opex_m*N + # for i in pumps: + # p = getattr(self, f'{i}_pump') + # p_cost = p.baseline_purchase_costs + # p_add_opex = p.add_OPEX + # pump_cost += p_cost['Pump'] + # building_cost += p_cost['Pump building'] + # opex_o += p_add_opex['Pump operating'] + # opex_m += p_add_opex['Pump maintenance'] + + # N = D['Number of clarifiers'] + # C['Pumps'] = pump_cost*N + # C['Pump building'] = building_cost*N + # add_OPEX['Pump operating'] = opex_o*N + # add_OPEX['Pump maintenance'] = opex_m*N - # Power - pumping = 0. - for ID in self.pumps: - p = getattr(self, f'{ID}_pump') - if p is None: - continue - pumping += p.power_utility.rate + # # Power + # pumping = 0. + # for ID in self.pumps: + # p = getattr(self, f'{ID}_pump') + # if p is None: + # continue + # pumping += p.power_utility.rate - self.power_utility.rate += pumping*N - # self.power_utility.rate += scraper_power + # self.power_utility.rate += pumping*N + # # self.power_utility.rate += scraper_power diff --git a/qsdsan/sanunits/_sludge_treatment.py b/qsdsan/sanunits/_sludge_treatment.py index a349adbd..c2ffe82a 100644 --- a/qsdsan/sanunits/_sludge_treatment.py +++ b/qsdsan/sanunits/_sludge_treatment.py @@ -171,12 +171,12 @@ class Thickener(SanUnit): _ins_size_is_fixed = False _outs_size_is_fixed = False - # Costs - wall_concrete_unit_cost = 1081.73 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) - slab_concrete_unit_cost = 582.48 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) - stainless_steel_unit_cost=1.8 # Alibaba. Brushed Stainless Steel Plate 304. https://www.alibaba.com/product-detail/brushed-stainless-steel-plate-304l-stainless_1600391656401.html?spm=a2700.details.0.0.230e67e6IKwwFd + # # Costs + # wall_concrete_unit_cost = 1081.73 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) + # slab_concrete_unit_cost = 582.48 # $/m3 (Hydromantis. CapdetWorks 4.0. https://www.hydromantis.com/CapdetWorks.html) + # stainless_steel_unit_cost=1.8 # Alibaba. Brushed Stainless Steel Plate 304. https://www.alibaba.com/product-detail/brushed-stainless-steel-plate-304l-stainless_1600391656401.html?spm=a2700.details.0.0.230e67e6IKwwFd - pumps = ('sludge',) + # pumps = ('sludge',) def __init__(self, ID='', ins=None, outs=(), thermo=None, isdynamic=False, init_with='WasteStream', F_BM_default=default_F_BM, thickener_perc=7, @@ -258,8 +258,6 @@ def Qu_factor(self): return f_Qu def _update_parameters(self): - # Thickener_factor, Thinning_factor, and Qu_factor need to be - # updated again and again. while dynamic simulations cmps = self.components TSS_in = np.sum(self._state[:-1]*cmps.i_mass*cmps.x) self._f_thick = f_thick = calc_f_thick(self._tp, TSS_in) @@ -281,18 +279,10 @@ def _run(self): mixed.split_to(uf, of, split_to_uf) def _init_state(self): - - # This function is run only once during dynamic simulations - - # Since there could be multiple influents, the state of the unit is - # obtained assuming perfect mixing Qs = self._ins_QC[:,-1] Cs = self._ins_QC[:,:-1] self._state = np.append(Qs @ Cs / Qs.sum(), Qs.sum()) self._dstate = self._state * 0. - - # To initialize the updated_thickener_factor, updated_thinning_factor - # and updated_Qu_factor for dynamic simulation self._update_parameters() def _update_state(self): @@ -315,8 +305,7 @@ def _update_state(self): # For sludge, the particulate concentrations (x) are multipled by thickener factor, and # flowrate is multiplied by Qu_factor. The soluble concentrations (1-x) remains same. uf.state[:-1] = arr[:-1] * ((1-x) + x*thickener_factor) - uf.state[-1] = arr[-1] * Qu_factor - + uf.state[-1] = arr[-1] * Qu_factor # For effluent, the particulate concentrations (x) are multipled by thinning factor, and # flowrate is multiplied by Qu_factor. The soluble concentrations (1-x) remains same. of.state[:-1] = arr[:-1] * ((1-x) + x*thinning_factor) @@ -380,247 +369,238 @@ def yt(t, QC_ins, dQC_ins): _update_dstate() self._AE = yt - def _design_pump(self): - ID, pumps = self.ID, self.pumps - self._sludge.copy_like(self.outs[0]) - sludge = self._sludge + # def _design_pump(self): + # ID, pumps = self.ID, self.pumps + # self._sludge.copy_like(self.outs[0]) + # sludge = self._sludge - ins_dct = { - 'sludge': sludge, - } + # ins_dct = { + # 'sludge': sludge, + # } - type_dct = dict.fromkeys(pumps, 'sludge') - inputs_dct = dict.fromkeys(pumps, (1,)) + # type_dct = dict.fromkeys(pumps, 'sludge') + # inputs_dct = dict.fromkeys(pumps, (1,)) - D = self.design_results - influent_Q = sludge.get_total_flow('m3/hr')/D['Number of thickeners'] - influent_Q_mgd = influent_Q*0.00634 # m3/hr to MGD + # D = self.design_results + # influent_Q = sludge.get_total_flow('m3/hr')/D['Number of thickeners'] + # influent_Q_mgd = influent_Q*0.00634 # m3/hr to MGD - for i in pumps: - if hasattr(self, f'{i}_pump'): - p = getattr(self, f'{i}_pump') - setattr(p, 'add_inputs', inputs_dct[i]) - else: - ID = f'{ID}_{i}' - capacity_factor=1 - # No. of pumps = No. of influents - pump = WWTpump( - ID=ID, ins= ins_dct[i], thermo = self.thermo, pump_type=type_dct[i], - Q_mgd=influent_Q_mgd, add_inputs=inputs_dct[i], - capacity_factor=capacity_factor, - include_pump_cost=True, - include_building_cost=False, - include_OM_cost=True, - ) - setattr(self, f'{i}_pump', pump) + # for i in pumps: + # if hasattr(self, f'{i}_pump'): + # p = getattr(self, f'{i}_pump') + # setattr(p, 'add_inputs', inputs_dct[i]) + # else: + # ID = f'{ID}_{i}' + # capacity_factor=1 + # # No. of pumps = No. of influents + # pump = WWTpump( + # ID=ID, ins= ins_dct[i], thermo = self.thermo, pump_type=type_dct[i], + # Q_mgd=influent_Q_mgd, add_inputs=inputs_dct[i], + # capacity_factor=capacity_factor, + # include_pump_cost=True, + # include_building_cost=False, + # include_OM_cost=True, + # ) + # setattr(self, f'{i}_pump', pump) - pipe_ss, pump_ss = 0., 0. - for i in pumps: - p = getattr(self, f'{i}_pump') - p.simulate() - p_design = p.design_results - pipe_ss += p_design['Pump pipe stainless steel'] - pump_ss += p_design['Pump stainless steel'] - return pipe_ss, pump_ss + # pipe_ss, pump_ss = 0., 0. + # for i in pumps: + # p = getattr(self, f'{i}_pump') + # p.simulate() + # p_design = p.design_results + # pipe_ss += p_design['Pump pipe stainless steel'] + # pump_ss += p_design['Pump stainless steel'] + # return pipe_ss, pump_ss - _units = { - 'Design solids loading rate': 'kg/m2/hr', - 'Total mass of solids handled': 'kg', - 'Surface area': 'm2', - 'Thickener diameter': 'm', - 'Number of thickeners' : 'Unitless', - 'Thickener depth': 'm', - 'Conical depth': 'm', - 'Cylindrical depth': 'm', - 'Cylindrical volume': 'm3', - 'Conical volume': 'm3', - 'Thickener volume': 'm3', - - 'Center feed depth': 'm', - 'Downward flow velocity': 'm/hr', - 'Volumetric flow': 'm3/hr', - 'Center feed diameter': 'm', - 'Thickener depth': 'm', - 'Volume of concrete wall': 'm3', - 'Volume of concrete slab': 'm3', - 'Stainless steel': 'kg', - 'Pump pipe stainless steel' : 'kg', - 'Pump stainless steel': 'kg', - 'Number of pumps': 'Unitless' - } + # _units = { + # 'Design solids loading rate': 'kg/m2/hr', + # 'Total mass of solids handled': 'kg', + # 'Surface area': 'm2', + # 'Thickener diameter': 'm', + # 'Number of thickeners' : 'Unitless', + # 'Thickener depth': 'm', + # 'Conical depth': 'm', + # 'Cylindrical depth': 'm', + # 'Cylindrical volume': 'm3', + # 'Conical volume': 'm3', + # 'Thickener volume': 'm3', + + # 'Center feed depth': 'm', + # 'Downward flow velocity': 'm/hr', + # 'Volumetric flow': 'm3/hr', + # 'Center feed diameter': 'm', + # 'Thickener depth': 'm', + # 'Volume of concrete wall': 'm3', + # 'Volume of concrete slab': 'm3', + # 'Stainless steel': 'kg', + # 'Pump pipe stainless steel' : 'kg', + # 'Pump stainless steel': 'kg', + # 'Number of pumps': 'Unitless' + # } - def _design(self): - - self._mixed.mix_from(self.ins) - mixed = self._mixed - D = self.design_results - - # D['Number of thickeners'] = np.ceil(self._mixed.get_total_flow('m3/hr')/self.design_flow) - D['Design solids loading rate'] = self.solids_loading_rate # in (kg/hr)/m2 - D['Total mass of solids handled'] = (mixed.get_TSS()/1000)*mixed.get_total_flow('m3/hr') # (mg/L)*[1/1000(kg*L)/(mg*m3)](m3/hr) = (kg/hr) - - # Common gravity thickener configurations have tanks with diameter between 21-24m (MOP 8) - diameter_thickener = 24 - number_of_thickeners = 0 - while diameter_thickener >= 22: - number_of_thickeners += 1 - total_surface_area = D['Total mass of solids handled']/D['Design solids loading rate'] #m2 - surface_area_thickener = total_surface_area/number_of_thickeners - diameter_thickener = np.sqrt(4*surface_area_thickener/np.pi) + # def _design(self): + + # self._mixed.mix_from(self.ins) + # mixed = self._mixed + # D = self.design_results + + # # D['Number of thickeners'] = np.ceil(self._mixed.get_total_flow('m3/hr')/self.design_flow) + # D['Design solids loading rate'] = self.solids_loading_rate # in (kg/hr)/m2 + # D['Total mass of solids handled'] = (mixed.get_TSS()/1000)*mixed.get_total_flow('m3/hr') # (mg/L)*[1/1000(kg*L)/(mg*m3)](m3/hr) = (kg/hr) + + # # Common gravity thickener configurations have tanks with diameter between 21-24m (MOP 8) + # diameter_thickener = 24 + # number_of_thickeners = 0 + # while diameter_thickener >= 22: + # number_of_thickeners += 1 + # total_surface_area = D['Total mass of solids handled']/D['Design solids loading rate'] #m2 + # surface_area_thickener = total_surface_area/number_of_thickeners + # diameter_thickener = np.sqrt(4*surface_area_thickener/np.pi) - D['Surface area'] = surface_area_thickener #in m2 - D['Thickener diameter'] = diameter_thickener #in m - D['Number of thickeners'] = number_of_thickeners + # D['Surface area'] = surface_area_thickener #in m2 + # D['Thickener diameter'] = diameter_thickener #in m + # D['Number of thickeners'] = number_of_thickeners - # Common gravity thickener configurations have sidewater depth between 3-4m (MOP 8) - D['Thickener depth'] = self.h_thickener #in m - # The thickener tank floor generally has slope between 2:12 and 3:12 (MOP 8) - D['Conical depth'] = (2/12)*(D['Thickener diameter']/2) - D['Cylindrical depth'] = D['Thickener depth'] - D['Conical depth'] - - # Checks on depth - if D['Cylindrical depth'] < 0: - cyl_depth = D['Cylindrical depth'] - RuntimeError(f'Cylindrical depth (= {cyl_depth} ) is negative') + # # Common gravity thickener configurations have sidewater depth between 3-4m (MOP 8) + # D['Thickener depth'] = self.h_thickener #in m + # # The thickener tank floor generally has slope between 2:12 and 3:12 (MOP 8) + # D['Conical depth'] = (2/12)*(D['Thickener diameter']/2) + # D['Cylindrical depth'] = D['Thickener depth'] - D['Conical depth'] + + # # Checks on depth + # if D['Cylindrical depth'] < 0: + # cyl_depth = D['Cylindrical depth'] + # RuntimeError(f'Cylindrical depth (= {cyl_depth} ) is negative') - if D['Cylindrical depth'] < D['Conical depth']: - cyl_depth = D['Cylindrical depth'] - con_depth = D['Conical depth'] - RuntimeError(f'Cylindrical depth (= {cyl_depth} ) is lower than Conical depth (= {con_depth})') + # if D['Cylindrical depth'] < D['Conical depth']: + # cyl_depth = D['Cylindrical depth'] + # con_depth = D['Conical depth'] + # RuntimeError(f'Cylindrical depth (= {cyl_depth} ) is lower than Conical depth (= {con_depth})') - D['Cylindrical volume'] = np.pi*np.square(D['Thickener diameter']/2)*D['Cylindrical depth'] #in m3 - D['Conical volume'] = (np.pi/3)*(D['Thickener diameter']/2)**2*D['Conical depth'] - D['Thickener volume'] = D['Cylindrical volume'] + D['Conical volume'] - - #Check on SOR is pending - - # The design here is for center feed of thickener. - # Depth of the center feed lies between 30-75% of sidewater depth. [2] - D['Center feed depth'] = 0.5*D['Cylindrical depth'] - # Typical conventional feed wells are designed for an average downflow velocity - # of 10-13 mm/s and maximum velocity of 25-30 mm/s. [4] - peak_flow_safety_factor = 2.5 # assumed based on average and maximum velocities - D['Downward flow velocity'] = self.downward_flow_velocity*peak_flow_safety_factor # in m/hr - - D['Volumetric flow'] = mixed.get_total_flow('m3/hr')/D['Number of thickeners'] # m3/hr - Center_feed_area = D['Volumetric flow']/D['Downward flow velocity'] # in m2 - D['Center feed diameter'] = np.sqrt(4*Center_feed_area/np.pi) # in m + # D['Cylindrical volume'] = np.pi*np.square(D['Thickener diameter']/2)*D['Cylindrical depth'] #in m3 + # D['Conical volume'] = (np.pi/3)*(D['Thickener diameter']/2)**2*D['Conical depth'] + # D['Thickener volume'] = D['Cylindrical volume'] + D['Conical volume'] + + # #Check on SOR is pending + + # # The design here is for center feed of thickener. + # # Depth of the center feed lies between 30-75% of sidewater depth. [2] + # D['Center feed depth'] = 0.5*D['Cylindrical depth'] + # # Typical conventional feed wells are designed for an average downflow velocity + # # of 10-13 mm/s and maximum velocity of 25-30 mm/s. [4] + # peak_flow_safety_factor = 2.5 # assumed based on average and maximum velocities + # D['Downward flow velocity'] = self.downward_flow_velocity*peak_flow_safety_factor # in m/hr + + # D['Volumetric flow'] = mixed.get_total_flow('m3/hr')/D['Number of thickeners'] # m3/hr + # Center_feed_area = D['Volumetric flow']/D['Downward flow velocity'] # in m2 + # D['Center feed diameter'] = np.sqrt(4*Center_feed_area/np.pi) # in m - #Diameter of the center feed does not exceed 40% of tank diameter [2] - if D['Center feed diameter'] > 0.40*D['Thickener diameter']: - cf_dia = D['Center feed diameter'] - tank_dia = D['Thickener diameter'] - warn(f'Diameter of the center feed exceeds 40% of tank diameter. It is {cf_dia*100/tank_dia}% of tank diameter') + # #Diameter of the center feed does not exceed 40% of tank diameter [2] + # if D['Center feed diameter'] > 0.40*D['Thickener diameter']: + # cf_dia = D['Center feed diameter'] + # tank_dia = D['Thickener diameter'] + # warn(f'Diameter of the center feed exceeds 40% of tank diameter. It is {cf_dia*100/tank_dia}% of tank diameter') - # Amount of concrete required - D_tank = D['Thickener depth']*39.37 # m to inches - # Thickness of the wall concrete [m]. Default to be minimum of 1 feet with 1 inch added for every feet of depth over 12 feet. - thickness_concrete_wall = (1 + max(D_tank-12, 0)/12)*0.3048 # from feet to m - inner_diameter = D['Thickener diameter'] - outer_diameter = inner_diameter + 2*thickness_concrete_wall - volume_cylindercal_wall = (np.pi*D['Cylindrical depth']/4)*(outer_diameter**2 - inner_diameter**2) - D['Volume of concrete wall'] = volume_cylindercal_wall # in m3 - - # Concrete slab thickness, [ft], default to be 2 in thicker than the wall thickness. (Brian's code) - thickness_concrete_slab = thickness_concrete_wall + (2/12)*0.3048 # from inch to m - outer_diameter_cone = inner_diameter + 2*(thickness_concrete_wall + thickness_concrete_slab) - volume_conical_wall = (np.pi/(3*4))*(((D['Conical depth'] + thickness_concrete_wall + thickness_concrete_slab)*(outer_diameter_cone**2)) - (D['Conical depth']*(inner_diameter)**2)) - D['Volume of concrete slab'] = volume_conical_wall - - # Amount of metal required for center feed - thickness_metal_wall = 0.3048 # equal to 1 feet, in m (!! NEED A RELIABLE SOURCE !!) - inner_diameter_center_feed = D['Center feed diameter'] - outer_diameter_center_feed = inner_diameter_center_feed + 2*thickness_metal_wall - volume_center_feed = (3.14*D['Center feed depth']/4)*(outer_diameter_center_feed**2 - inner_diameter_center_feed**2) - density_ss = 7930 # kg/m3, 18/8 Chromium - D['Stainless steel'] = volume_center_feed*density_ss # in kg + # # Amount of concrete required + # D_tank = D['Thickener depth']*39.37 # m to inches + # # Thickness of the wall concrete [m]. Default to be minimum of 1 feet with 1 inch added for every feet of depth over 12 feet. + # thickness_concrete_wall = (1 + max(D_tank-12, 0)/12)*0.3048 # from feet to m + # inner_diameter = D['Thickener diameter'] + # outer_diameter = inner_diameter + 2*thickness_concrete_wall + # volume_cylindercal_wall = (np.pi*D['Cylindrical depth']/4)*(outer_diameter**2 - inner_diameter**2) + # D['Volume of concrete wall'] = volume_cylindercal_wall # in m3 + + # # Concrete slab thickness, [ft], default to be 2 in thicker than the wall thickness. (Brian's code) + # thickness_concrete_slab = thickness_concrete_wall + (2/12)*0.3048 # from inch to m + # outer_diameter_cone = inner_diameter + 2*(thickness_concrete_wall + thickness_concrete_slab) + # volume_conical_wall = (np.pi/(3*4))*(((D['Conical depth'] + thickness_concrete_wall + thickness_concrete_slab)*(outer_diameter_cone**2)) - (D['Conical depth']*(inner_diameter)**2)) + # D['Volume of concrete slab'] = volume_conical_wall + + # # Amount of metal required for center feed + # thickness_metal_wall = 0.3048 # equal to 1 feet, in m (!! NEED A RELIABLE SOURCE !!) + # inner_diameter_center_feed = D['Center feed diameter'] + # outer_diameter_center_feed = inner_diameter_center_feed + 2*thickness_metal_wall + # volume_center_feed = (3.14*D['Center feed depth']/4)*(outer_diameter_center_feed**2 - inner_diameter_center_feed**2) + # density_ss = 7930 # kg/m3, 18/8 Chromium + # D['Stainless steel'] = volume_center_feed*density_ss # in kg - # Pumps - pipe, pumps = self._design_pump() - D['Pump pipe stainless steel'] = pipe - D['Pump stainless steel'] = pumps + # # Pumps + # pipe, pumps = self._design_pump() + # D['Pump pipe stainless steel'] = pipe + # D['Pump stainless steel'] = pumps - #For thickener - D['Number of pumps'] = D['Number of thickeners'] + # #For thickener + # D['Number of pumps'] = D['Number of thickeners'] - def _cost(self): + # def _cost(self): - self._mixed.mix_from(self.ins) - mixed = self._mixed + # self._mixed.mix_from(self.ins) + # mixed = self._mixed - D = self.design_results - C = self.baseline_purchase_costs + # D = self.design_results + # C = self.baseline_purchase_costs - # Construction of concrete and stainless steel walls - C['Wall concrete'] = D['Number of thickeners']*D['Volume of concrete wall']*self.wall_concrete_unit_cost - C['Slab concrete'] = D['Number of thickeners']*D['Volume of concrete slab']*self.slab_concrete_unit_cost - C['Wall stainless steel'] = D['Number of thickeners']*D['Stainless steel']*self.stainless_steel_unit_cost + # # Construction of concrete and stainless steel walls + # C['Wall concrete'] = D['Number of thickeners']*D['Volume of concrete wall']*self.wall_concrete_unit_cost + # C['Slab concrete'] = D['Number of thickeners']*D['Volume of concrete slab']*self.slab_concrete_unit_cost + # C['Wall stainless steel'] = D['Number of thickeners']*D['Stainless steel']*self.stainless_steel_unit_cost - # Cost of equipment - # Source of scaling exponents: Process Design and Economics for Biochemical Conversion of Lignocellulosic Biomass to Ethanol by NREL. - - # Scraper - # Source: https://www.alibaba.com/product-detail/Peripheral-driving-clarifier-mud-scraper-waste_1600891102019.html?spm=a2700.details.0.0.47ab45a4TP0DLb - # base_cost_scraper = 2500 - # base_flow_scraper = 1 # in m3/hr (!!! Need to know whether this is for solids or influent !!!) - thickener_flow = mixed.get_total_flow('m3/hr')/D['Number of thickeners'] - # C['Scraper'] = D['Number of thickeners']*base_cost_scraper*(thickener_flow/base_flow_scraper)**0.6 - # base_power_scraper = 2.75 # in kW - # THE EQUATION BELOW IS NOT CORRECT TO SCALE SCRAPER POWER REQUIREMENTS - # scraper_power = D['Number of thickeners']*base_power_scraper*(thickener_flow/base_flow_scraper)**0.6 - - # v notch weir - # Source: https://www.alibaba.com/product-detail/50mm-Tube-Settler-Media-Modules-Inclined_1600835845218.html?spm=a2700.galleryofferlist.normal_offer.d_title.69135ff6o4kFPb - base_cost_v_notch_weir = 6888 - base_flow_v_notch_weir = 10 # in m3/hr - C['v notch weir'] = D['Number of thickeners']*base_cost_v_notch_weir*(thickener_flow/base_flow_v_notch_weir)**0.6 + # # Cost of equipment + # # Source of scaling exponents: Process Design and Economics for Biochemical Conversion of Lignocellulosic Biomass to Ethanol by NREL. + + # # Scraper + # # Source: https://www.alibaba.com/product-detail/Peripheral-driving-clarifier-mud-scraper-waste_1600891102019.html?spm=a2700.details.0.0.47ab45a4TP0DLb + # # base_cost_scraper = 2500 + # # base_flow_scraper = 1 # in m3/hr (!!! Need to know whether this is for solids or influent !!!) + # thickener_flow = mixed.get_total_flow('m3/hr')/D['Number of thickeners'] + # # C['Scraper'] = D['Number of thickeners']*base_cost_scraper*(thickener_flow/base_flow_scraper)**0.6 + # # base_power_scraper = 2.75 # in kW + # # THE EQUATION BELOW IS NOT CORRECT TO SCALE SCRAPER POWER REQUIREMENTS + # # scraper_power = D['Number of thickeners']*base_power_scraper*(thickener_flow/base_flow_scraper)**0.6 + + # # v notch weir + # # Source: https://www.alibaba.com/product-detail/50mm-Tube-Settler-Media-Modules-Inclined_1600835845218.html?spm=a2700.galleryofferlist.normal_offer.d_title.69135ff6o4kFPb + # base_cost_v_notch_weir = 6888 + # base_flow_v_notch_weir = 10 # in m3/hr + # C['v notch weir'] = D['Number of thickeners']*base_cost_v_notch_weir*(thickener_flow/base_flow_v_notch_weir)**0.6 - # Pump (construction and maintainance) - pumps = self.pumps - add_OPEX = self.add_OPEX - pump_cost = 0. - building_cost = 0. - opex_o = 0. - opex_m = 0. + # # Pump (construction and maintainance) + # pumps = self.pumps + # add_OPEX = self.add_OPEX + # pump_cost = 0. + # building_cost = 0. + # opex_o = 0. + # opex_m = 0. - for i in pumps: - p = getattr(self, f'{i}_pump') - p_cost = p.baseline_purchase_costs - p_add_opex = p.add_OPEX - pump_cost += p_cost['Pump'] - building_cost += p_cost['Pump building'] - opex_o += p_add_opex['Pump operating'] - opex_m += p_add_opex['Pump maintenance'] + # for i in pumps: + # p = getattr(self, f'{i}_pump') + # p_cost = p.baseline_purchase_costs + # p_add_opex = p.add_OPEX + # pump_cost += p_cost['Pump'] + # building_cost += p_cost['Pump building'] + # opex_o += p_add_opex['Pump operating'] + # opex_m += p_add_opex['Pump maintenance'] - C['Pumps'] = pump_cost*D['Number of thickeners'] - C['Pump building'] = building_cost*D['Number of thickeners'] - add_OPEX['Pump operating'] = opex_o*D['Number of thickeners'] - add_OPEX['Pump maintenance'] = opex_m*D['Number of thickeners'] + # C['Pumps'] = pump_cost*D['Number of thickeners'] + # C['Pump building'] = building_cost*D['Number of thickeners'] + # add_OPEX['Pump operating'] = opex_o*D['Number of thickeners'] + # add_OPEX['Pump maintenance'] = opex_m*D['Number of thickeners'] - # Power - pumping = 0. - for ID in self.pumps: - p = getattr(self, f'{ID}_pump') - if p is None: - continue - pumping += p.power_utility.rate - - pumping = pumping*D['Number of thickeners'] - - self.power_utility.rate += pumping - # self.power_utility.rate += scraper_power + # # Power + # pumping = 0. + # for ID in self.pumps: + # p = getattr(self, f'{ID}_pump') + # if p is None: + # continue + # pumping += p.power_utility.rate + + # pumping = pumping*D['Number of thickeners'] + + # self.power_utility.rate += pumping + # # self.power_utility.rate += scraper_power #%% Centrifuge -# Asign a bare module of 1 to all - -# default_F_BM = { -# 'Bowl stainless steel': 1, -# 'Conveyor': 1, -# 'Pumps': 1, -# } -# default_F_BM.update(default_WWTpump_F_BM) - class Centrifuge(Thickener): """ @@ -867,6 +847,7 @@ def _cost(self): pumping = pumping*D['Number of pumps'] self.power_utility.rate += pumping self.power_utility.rate += total_motor_power + #%% Incinerator class Incinerator(SanUnit):