implementation of Robinson, Walter A. (2001) Modeling Dynamic Climate Systems. (section 2.2)
<?xml version="1.0" encoding="UTF-8"?>
<!--Xholon Workbook http://www.primordion.com/Xholon/wb/ (C) Ken Webb Tue May 08 2012 17:13:45 GMT-0400 (EDT)-->
<XholonWorkbook>
<Notes><![CDATA[
Xholon
------
Title: One-Layer Atmosphere Model - Simplified Code
Description: implementation of Robinson, Walter A. (2001) Modeling Dynamic Climate Systems. (section 2.2)
Url: http://www.primordion.com/Xholon/wb/xholon
InternalName:
YoutubeId:
Keywords:
My Notes
--------
How to run this app ::
Click the Run button above.
The JavaScript code is experimental only and won't execute.
This workbook implements Robinson's One-Layer Atmosphere Model, using an experimental coding syntax. This syntax is much simpler than the working JavaScript + jQuery version at ::
https://gist.github.com/2489058
http://www.primordion.com/Xholon/wb/openwb.php?q=2489058&f=gist.github.com/raw/
The simplified code is more like pseudocode. It has just the essential domain-specific information necessary to code the application in some other language. For example, you should be able to implement the app with a calculator or a spreadsheet.
On the other hand, the simplified code should be formal enough that it could be automatically converted into a real programming or scripting language such as Java or JavaScript, and then executed.
These behaviors depend on having the hierarchical structure as defined in the XML editors on this page (see XholonClass, xholonClassDetails, TheSystem editors). Certain conventions are used to ensure that variables in the behaviors can be bound to the correct nodes in the structure. For example, the Atmosphere node has various child nodes that contain physical properties ::
Energy, Albedo, Temperature, Mass, etc.
It's necessary that the corresponding variables in the Atmosphere behavior have these names ::
energy, albedo, temperature, mass
These default conventions can be circumvented using ports specified in the xholonClassDetails.
Other possible conventions for a webEdition (JavaScript, jQuery, HTML) runtime ::
* Numeric data is saved to and retrieved from an HTML attribute, using jQuery .attr()
ex: solar = solarIn * (1.0 - albedo)
* References to other HTML elements (ports), are saved and retrieved using jQuery/HTML5 .data()
ex:
* Note:
It could be ambiguous whether a user wants to save an element or the value of an element.
There needs to be a convention about this.
Probably assume that the value of the element is intended, unless otherwise specfied.
ex: a = b means assign the value of b as the value of a, which is the normal assumption for variables
so if b has a value of 123, then a will have a value of 123 after the assignment
jQuery .attr("a", b)
The C language uses & to get the address of an object, rather than its value
Or possibly use a function such as port(a, b) to mean jQuery .data("a", b)
* If a local private variable is intended, use "var" or "def"
ex: var answer = 17 + 25
* Use the actual class name of a node in the XML header for a behavior, rather than suffixing it with "behavior"
ex: <Sun> rather than <SunBehavior>
Some possible sources of ideas ::
* CoffeeScript is a simplified language that compiles to JavaScript
* Fantom compiles to JavaScript, and runs on Java VM and .Net CLR
* Groovy is a Java-like language with a simplified syntax
]]></Notes>
<script implName="lang:python:inline:"><![CDATA[
]]></script>
<script implName="lang:javascript:inline:"><![CDATA[
]]></script>
<_-.XholonClass>
<!-- types of domain objects -->
<TheSystem/>
<SolarSystem/>
<Sun/>
<Planet>
<Earth/>
</Planet>
<Space/>
<TopOfAtmosphere/>
<Atmosphere/>
<Surface/> <!-- "the planet is represented by an average square meter of its surface"; ocean -->
<Water/> <!-- 70% of the Earth's surface is water; for the model we assume 100% of the surface is water -->
<MixedLayer/> <!-- the top layer of the ocean, where significant mixing of heat/etc occurs -->
<!-- quantities -->
<PhysicalProperty>
<!-- properties of the atmosphere -->
<Mass/>
<Absorption/>
<Emissivity/>
<!-- properties of the square meter of surface -->
<Energy/>
<Albedo/>
<Temperature/> <!-- Kelvin -->
<Celsius/> <!-- Temperature in Celsius -->
<!-- properties of the water -->
<Density/>
<SpecificHeat/>
<Depth/>
<HeatCapacity/>
</PhysicalProperty>
<!-- fluxes -->
<Flux/>
<Fluxes/>
<!-- constants -->
<Constant>
<StefanBoltzmannConstant/>
<SecondsPerTimeStep/>
</Constant>
<Constants/>
</_-.XholonClass>
<xholonClassDetails>
<Sun xhType="XhtypePureActiveObject">
<port name="space" connector="#xpointer(ancestor::SolarSystem/Space)"/>
</Sun>
<Space xhType="XhtypePureActiveObject">
<port name="planet" connector="#xpointer(ancestor::SolarSystem/Earth/TopOfAtmosphere)"/>
<port name="solarConstant" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='SunSpc_sw'])"/>
<port name="longwaveRadiation" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='TpfSpc_lw'])"/>
<port name="reflectedShortwaveRadiation" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='TpfSpc_sw'])"/>
</Space>
<TopOfAtmosphere xhType="XhtypePureActiveObject">
<port name="space" connector="#xpointer(ancestor::SolarSystem/Space)"/>
<port name="atmosphere" connector="#xpointer(ancestor::Earth/Atmosphere)"/>
<port name="albedo" connector="#xpointer(Albedo)"/>
<port name="solarConstant" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='SpcTpf_sw'])"/>
<port name="solarConstantDivFour" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='TpfTpf_sw'])"/>
<port name="reflectedShortwaveRadiation" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='AtmTpf_sw'])"/>
<port name="lwOut" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='AtmTpf_lw'])"/>
</TopOfAtmosphere>
<Atmosphere xhType="XhtypePureActiveObject">
<port name="topOfAtmosphere" connector="#xpointer(ancestor::Earth/TopOfAtmosphere)"/>
<port name="surface" connector="#xpointer(ancestor::Earth/Surface)"/>
<port name="energy" connector="#xpointer(Energy)"/>
<port name="temperature" connector="#xpointer(Temperature)"/>
<port name="albedo" connector="#xpointer(Albedo)"/>
<port name="mass" connector="#xpointer(Mass)"/>
<port name="specificHeat" connector="#xpointer(SpecificHeat)"/>
<port name="heatCapacity" connector="#xpointer(HeatCapacity)"/>
<port name="absorption" connector="#xpointer(Absorption)"/>
<port name="emissivity" connector="#xpointer(Emissivity)"/>
<port name="stefanBoltzmannConstant" connector="#xpointer(ancestor::TheSystem/Constants/StefanBoltzmannConstant)"/>
<port name="secondsPerTimeStep" connector="#xpointer(ancestor::TheSystem/Constants/SecondsPerTimeStep)"/>
<port name="swIn" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='TpfAtm_sw'])"/>
<port name="lwIn" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='SrfAtm_lw'])"/>
<port name="lwUp" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='AtmTpf_lw'])"/>
<port name="lwDown" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='AtmSrf_lw'])"/>
<port name="reflectedShortwaveRadiation" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='SrfAtm_sw'])"/>
</Atmosphere>
<Surface xhType="XhtypePureActiveObject">
<port name="atmosphere" connector="#xpointer(ancestor::Earth/Atmosphere)"/>
<port name="energy" connector="#xpointer(Energy)"/>
<port name="albedo" connector="#xpointer(Albedo)"/>
<port name="temperature" connector="#xpointer(Temperature)"/>
<port name="water" connector="#xpointer(Water)"/>
<port name="stefanBoltzmannConstant" connector="#xpointer(ancestor::TheSystem/Constants/StefanBoltzmannConstant)"/>
<port name="secondsPerTimeStep" connector="#xpointer(ancestor::TheSystem/Constants/SecondsPerTimeStep)"/>
<port name="solarIn" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='AtmSrf_sw'])"/>
<port name="infraredIn" connector="#xpointer(ancestor::TheSystem/Fluxes/Flux[@roleName='AtmSrf_lw'])"/>
</Surface>
<Water xhType="XhtypePureActiveObject">
<port name="depth" connector="#xpointer(MixedLayer/Depth)"/>
<port name="heatCapacity" connector="#xpointer(HeatCapacity)"/>
<port name="density" connector="#xpointer(Density)"/>
<port name="specificHeat" connector="#xpointer(SpecificHeat)"/>
</Water>
<Celsius xhType="XhtypePurePassiveObject">
<port name="k" connector="#xpointer(..)"/>
</Celsius>
</xholonClassDetails>
<TheSystem>
<Constants>
<StefanBoltzmannConstant unit="W/m^2/K^4">5.6696e-8</StefanBoltzmannConstant>
<SecondsPerTimeStep unit="s">31536000.0</SecondsPerTimeStep> <!-- SecondsPerYear 365*24*60*60 -->
<!--<SecondsPerTimeStep units="s">86400.0</SecondsPerTimeStep>--> <!-- SecondsPerDay -->
<!--<SecondsPerTimeStep units="s">2592000.0</SecondsPerTimeStep>--> <!-- SecondsPerMonth -->
</Constants>
<SolarSystem>
<Sun>
<Temperature unit="K">5778.0</Temperature>
</Sun>
<Space/>
<!--<xi:include href="./org/primordion/user/app/climatechange/mdcs/m2_2mp/earthRB_csh.xml"/>-->
<Earth>
<TopOfAtmosphere>
<Albedo>0.307168</Albedo> <!-- for use with Robinson STELLA approach -->
</TopOfAtmosphere>
<Atmosphere>
<Energy unit="J"/>
<Albedo>0.0</Albedo> <!-- 77/342 = 0.225146198 K+T -->
<Temperature unit="K">273.15
<Celsius roleName="Ta"/>
</Temperature> <!-- initial temperature -->
<Mass unit="kg/m^2">10326.19776</Mass> <!-- 101300/9.81 -->
<SpecificHeat unit="J/kg/K">1004.0</SpecificHeat>
<HeatCapacity unit="J/m^3/K"/>
<Absorption>0.1</Absorption> <!-- 67/(342-77) = 0.252830188 K+T -->
<Emissivity>0.856347</Emissivity>
</Atmosphere>
<Surface>
<Energy unit="J"/>
<Albedo>0.0</Albedo> <!-- 30/198 = 0.1515 K+T -->
<Temperature unit="K">273.15
<Celsius roleName="Ts"/>
</Temperature> <!-- initial temperature -->
<Water>
<Density unit="kg/m^3">1000.0</Density>
<SpecificHeat unit="J/kg/K">4218.0</SpecificHeat>
<MixedLayer>
<Depth unit="m">50.0</Depth>
</MixedLayer>
<HeatCapacity unit="J/m^3/K"/>
</Water>
</Surface>
</Earth>
</SolarSystem>
<Fluxes> <!-- with optional initial values -->
<!-- sw -->
<Flux roleName="SunSpc_sw" unit="W/m^2">1367.0</Flux> <!-- solar constant -->
<Flux roleName="SpcTpf_sw" unit="W/m^2">1367.0</Flux> <!-- solar constant -->
<Flux roleName="TpfTpf_sw" unit="W/m^2">341.75</Flux> <!-- local to Tpf; solar constant / 4 -->
<Flux roleName="TpfAtm_sw" unit="W/m^2">236.775336</Flux> <!-- solar KT:341.75 RB:236.775336 -->
<Flux roleName="AtmTpf_sw" unit="W/m^2">0.0</Flux> <!-- reflected -->
<Flux roleName="AtmSrf_sw" unit="W/m^2">213.0978024</Flux> <!-- solar KT:341.75 RB:213.0978024 -->
<Flux roleName="SrfAtm_sw" unit="W/m^2">0.0</Flux> <!-- reflected -->
<Flux roleName="TpfSpc_sw" unit="W/m^2">0.0</Flux> <!-- reflected -->
<!-- lw -->
<Flux roleName="SrfAtm_sh" unit="W/m^2">0.0</Flux>
<Flux roleName="SrfAtm_lh" unit="W/m^2">0.0</Flux>
<Flux roleName="SrfAtm_lw" unit="W/m^2">278.0</Flux>
<Flux roleName="AtmSrf_lw" unit="W/m^2">0.0</Flux>
<Flux roleName="AtmTpf_aw" unit="W/m^2">0.0</Flux>
<Flux roleName="AtmTpf_lw" unit="W/m^2">0.0</Flux>
<Flux roleName="TpfSpc_lw" unit="W/m^2">0.0</Flux>
</Fluxes>
</TheSystem>
<Blockbehavior implName="lang:python:inline:"><![CDATA[
]]></Blockbehavior>
<Blockbehavior implName="lang:javascript:inline:"><![CDATA[
]]></Blockbehavior>
<TheSystembehavior implName="lang:webEditionjs:inline:"><![CDATA[
function postConfigure() {
$("textarea#btstrp_console").css("font-size", "12px");
$("textarea#btstrp_console").attr("rows", "3");
$("textarea#btstrp_console").attr("cols", "50");
this.application('setConfigParam', 'TimeStepMultiplier', 32); // 32
dt = 1 / this.application('getConfigParam', 'TimeStepMultiplier');
}
function preAct() {
print("\n\nYear: " + this.application('getTimeStep'));
}
]]></TheSystembehavior>
<Sun implName="lang:webEditionjs:inline:"><![CDATA[
function postConfigure() {
solarConstant = 1367.0
}
function preAct() {
space.sendMessage(SIG_SW, solarConstant, this)
}
]]></Sun>
<Space implName="lang:webEditionjs:inline:"><![CDATA[
function postAct() {
planet.sendMessage(SIG_SW, solarConstant, this)
}
function processReceivedMessage(msg) {
switch (msg.signal) {
case SIG_SW:
solarConstant = msg.data
break
case SIG_LW:
longwaveRadiation = msg.data
break
case SIG_SW_REFLECTED:
reflectedShortwaveRadiation = msg.data
break
default: break
}
}
]]></Space>
<TopOfAtmosphere implName="lang:webEditionjs:inline:"><![CDATA[
function act() {
solarConstantDivFour = solarConstant / 4.0
atmosphere.sendMessage(SIG_SW, solarConstant / 4.0 * (1.0 - albedo), this)
if (albedo > 0.0) {
space.sendMessage(SIG_SW_REFLECTED, solarConstant / 4.0 * albedo, this)
}
}
function processReceivedMessage(msg) {
switch (msg.signal) {
case SIG_SW:
solarConstant = msg.data
break
case SIG_LW:
lwOut = msg.data // from atmosphere
space.sendMessage(msg.signal, msg.data, this) // relay
break
case SIG_SW_REFLECTED:
reflectedShortwaveRadiation = msg.data // from atmosphere
space.sendMessage(msg.signal, msg.data, this) // relay
break
default: break
}
}
]]></TopOfAtmosphere>
<Water implName="lang:webEditionjs:inline:"><![CDATA[
function postConfigure() {
heatCapacity = density * depth * specificHeat
}
]]></Water>
<Surface implName="lang:webEditionjs:inline:"><![CDATA[
function postConfigure() {
energy = temperature * water.heatCapacity
solar = solarIn * (1.0 - albedo)
infrared = stefanBoltzmannConstant * Math.pow(temperature, 4)
}
function act() {
energy = energy + (solar + infraredIn - infrared) * secondsPerTimeStep * dt
temperature = energy / water.heatCapacity
solar = solarIn * (1.0 - albedo)
infrared = stefanBoltzmannConstant * Math.pow(temperature, 4)
atmosphere.sendMessage(SIG_SW_REFLECTED, solarIn * albedo, this)
atmosphere.sendMessage(SIG_LW, infrared, this)
}
function processReceivedMessage(msg) {
switch (msg.signal) {
case SIG_SW:
solarIn = msg.data
break
case SIG_LW:
infraredIn = msg.data
break
default: break
}
}
]]></Surface>
<Atmosphere implName="lang:webEditionjs:inline:"><![CDATA[
function postConfigure() {
heatCapacity = mass * specificHeat
energy = temperature * heatCapacity
irAtmosphere = stefanBoltzmannConstant * emissivity * Math.pow(temperature, 4)
// ir is a function of surface temperature
ir = surface.infrared
var val1 = ir * (1.0 - emissivity
irToSpace = val1 + irAtmosphere
}
function act() {
reflectedFromAtmosphere = swIn * albedo
absorbedByAtmosphere = (swIn - reflectedFromAtmosphere) * absorption
energy = energy + (lwIn + absorbedByAtmosphere - irAtmosphere - irToSpace) * secondsPerTimeStep * dt
temperature = energy / heatCapacity
irAtmosphere = stefanBoltzmannConstant * emissivity * Math.pow(temperature, 4)
ir = lwIn
var val1 = ir * (1.0 - emissivity)
irToSpace = val1 + irAtmosphere
surface.sendMessage(SIG_SW, swIn - reflectedFromAtmosphere - absorbedByAtmosphere, this)
surface.sendMessage(SIG_LW, irAtmosphere, this)
topOfAtmosphere.sendMessage(SIG_LW, irToSpace, this)
// no need to send the next msg; it always has val = 0
//topOfAtmosphere.sendMessage(SIG_SW_REFLECTED, reflectedShortwaveRadiation + reflectedFromAtmosphere, this)
}
function processReceivedMessage(msg) {
switch(msg.signal) {
case SIG_SW:
swIn = msg.data
break
case SIG_LW:
lwIn = msg.data
break
case SIG_SW_REFLECTED:
reflectedShortwaveRadiation = msg.data // from surface
break
default:
break
}
}
]]></Atmosphere>
<Flux implName="lang:webEditionjs:inline:"><![CDATA[
function postAct() {
//print("\n" + rolename + ": " + val + " " + unit);
}
]]></Flux>
<Celsius implName="lang:webEditionjs:inline:"><![CDATA[
function postConfigure() {
var kelvin = k - 273.15
var celsius = kelvin - 273.15
print("\n" + roleName + " Celsius: " + celsius + " Kelvin: " + kelvin);
}
function act() {
// draw a graph
var oldCelsius = val
var newCelsius = this.parent().parent().attr("val") - 273.15;
val = newCelsius
var dCelsius = oldCelsius - newCelsius;
if (isNaN(dCelsius)) {dCelsius = 0;}
var path = this.parent().data("path" + roleName)
if (!path) {
var svg = $($('div#mySVGDiv > object')[0].contentDocument.getElementsByTagNameNS(svgns, 'svg')[0]);
path = svg.children("g").children("g#Graph").children("path#" + this.parent().attr("roleName"));
this.parent().data("path" + this.parent().attr("roleName"), path);
this.parent().data("d", path.attr("d"));
}
var d = this.parent().data("d") + "l1 " + (dCelsius * 5); // 10 5
this.parent().data("d", d);
path.attr("d", d);
}
function postAct() {
var kelvin = k
var celsius = kelvin - 273.15;
print("\n" + roleName + " Celsius: " + celsius.toFixed(3) + " Kelvin: " + kelvin.toFixed(3));
}
]]></Celsius>
<Blockbehavior implName="lang:bsh:inline:"><![CDATA[
]]></Blockbehavior>
<Blockbehavior implName="lang:jruby:inline:"><![CDATA[
]]></Blockbehavior>
<Blockbehavior implName="lang:groovy:inline:"><![CDATA[
]]></Blockbehavior>
<SvgClient><Attribute_String roleName="svgUri"><![CDATA[data:image/svg+xml,
<svg width="400" height="350" xmlns="http://www.w3.org/2000/svg">
<g>
<g id="Space">
<title>One-Layer Atmosphere Model - Space</title>
<rect id="TheSystem/SolarSystem/Space" fill="black" height="50" width="400"/>
<g>
<title>Sun</title>
<circle id="TheSystem/SolarSystem/Sun" fill="yellow" stroke="black" cx="15" cy="10" r="5"/>
</g>
<g>
<title>Atmosphere</title>
<circle id="TheSystem/SolarSystem/Earth/Atmosphere" fill="#4169E1" cx="370" cy="25" r="24"/>
</g>
<g>
<title>Surface with Water</title>
<circle id="TheSystem/SolarSystem/Earth/Surface/Water" fill="#3CB371" cx="370" cy="25" r="22"/>
</g>
<g>
<title>Earth</title>
<circle id="TheSystem/SolarSystem/Earth" opacity="0.25" fill="#CD853F" cx="370" cy="25" r="20"/>
</g>
</g>
<g id="Graph">
<title>Graph of Earth Surface and Atmosphere Temperatures vs Time</title>
<rect fill="black" y="50" height="300" width="400"/>
<path id="Ta" d="M0 150" stroke="white" stroke-width="1"/>
<path id="Ts" d="M0 150" stroke="white" stroke-width="1"/>
</g>
</g>
</svg>
]]></Attribute_String><Attribute_String roleName="setup">${MODELNAME_DEFAULT},${SVGURI_DEFAULT}</Attribute_String></SvgClient>
</XholonWorkbook>