Coverage for agent_model/agents/concrete.py: 100%

40 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-05-04 13:14 +0700

1from . import BaseAgent 

2from ..util import recursively_check_required_kwargs 

3import numpy as np 

4 

5class ConcreteAgent(BaseAgent): 

6 """One exposed square meter of carbonating concrete 

7 

8 ====================== ============== =============== 

9 Attribute Type Description 

10 ====================== ============== =============== 

11 ``carbonation_rate`` float Current co2-dependent carbonation rate, in kmoles 

12 ``carbonation`` float Total lifetime carbonation, in kmoles 

13 ====================== ============== =============== 

14 

15 This agent's exchange values are equal to the molar mass (g/mol) of the respective 

16 compounds, so when multiplied by the above attributes, the results are in kg. 

17 """ 

18 diffusion_rate = .000018 # Tune manually. Match Table 2 total kmoles 

19 saturation_when_measured = 0.3 # Tune manually. Lit suggests up to 20yr of carb. 

20 rate_scale = [12.7, 12.7 + 35 / saturation_when_measured] # Figure 2 integrals 

21 ppm_range = [350, 3000] # External and enclosed ppms 

22 density = 1.21 / 1000 # Table 2, 'structural concrete', convert grams to kg 

23 

24 default_attributes = { 

25 'carbonation_rate': 0, # Current kmoles/h rate 

26 'carbonation': 0, # Cumulative kmoles 

27 } 

28 

29 required_kwargs = { 

30 'flows': {'in': {'co2': 0, 'caoh2': 0}, 

31 'out': {'caco3': 0, 'moisture': 0}}, 

32 'capacity': {'caoh2': 0, 'caco3': 0, 'moisture': 0}} 

33 

34 def __init__(self, *args, attributes=None, **kwargs): 

35 recursively_check_required_kwargs(kwargs, self.required_kwargs) 

36 attributes = {} if attributes is None else attributes 

37 attributes = {**self.default_attributes, **attributes} 

38 super().__init__(*args, attributes=attributes, **kwargs) 

39 

40 # Set internal caoh2 to the maximum amount of carbonation at the highest ppm level 

41 caoh2_flow = self.flows['in']['caoh2']['value'] 

42 caco3_flow = self.flows['out']['caco3']['value'] 

43 moisture_flow = self.flows['out']['moisture']['value'] 

44 initial_storage = { 

45 'caoh2': self.calc_max_carbonation(3000) * caoh2_flow * self.active, 

46 'caco3': 0, # Byproduct, accumulates internally 

47 'moisture': 0, # Byproduct, accumulates internally 

48 } 

49 # If carbonation has already occured (Mission 2), update storages accordingly 

50 carbonation = self.attributes['carbonation'] 

51 if carbonation > 0: 

52 initial_storage['caoh2'] -= carbonation * caoh2_flow * self.active 

53 initial_storage['caco3'] += carbonation * caco3_flow * self.active 

54 initial_storage['moisture'] += carbonation * moisture_flow * self.active 

55 self.storage = {**self.storage, **initial_storage} 

56 

57 @classmethod 

58 def calc_max_carbonation(cls, ppm): 

59 """Return max kmoles CO2 uptake by structural concrete""" 

60 saturation_point_kmoles = np.interp(ppm, cls.ppm_range, cls.rate_scale) 

61 return saturation_point_kmoles * cls.density 

62 

63 def step(self, dT=1): 

64 """Set the carbonation rate, which is used to weight exchanges""" 

65 # Calculate ppm of CO2 in atmosphere 

66 ref_agent_name = self.flows['in']['co2']['connections'][0] 

67 ref_agent = self.model.agents[ref_agent_name] 

68 ref_atm = ref_agent.view('atmosphere') 

69 ppm = ref_atm['co2'] / sum(ref_atm.values()) * 1e6 

70 # Calculate carbonation rate 

71 max_carbonation = self.calc_max_carbonation(ppm) 

72 gradient = max(0, max_carbonation - self.attributes['carbonation']) 

73 self.attributes['carbonation_rate'] = gradient * self.diffusion_rate 

74 self.attributes['carbonation'] += self.attributes['carbonation_rate'] 

75 super().step(dT)