Connecting the declared input variables (global) to ExecComp - openmdao

Is there a way to connect the global input variables i.e.
def initialize(self):
self.options.declare('num_elements', types=int)
to an execcomp?
prob.model.add_subsystem('paraboloid', ExecComp('f = num_elements*3 + c'))

There isn't any way to connect to a declared option. The only things you can connect to are variables that were added inside components with add_input or add_output. I think in this case, since num_elements isn't meant to change, you should use a string expression to put the value into the ExecComp -- something like:
prob.model.add_subsystem('paraboloid', ExecComp('f = %d*3 + c' % num_elements))
where num_elements is a variable in your top level script.

Related

Define variable in problem which is not used as input, and equals a previous input/output

I would like to define a variable which, depending on certain options, will be equal to a previous output (as if the previous output had two names) or will be the output of a new component.
A trivial solution is to just omit the definition of the value when the component which would define it is not implemented, but I would prefer it to be defined for readability/traceability reasons (to simplify if statements in the code, and to provide it as timeseries output).
The problem is that when using the connect statement, if the subsequent condition does not lead to the variable being used as an input to another component, it provides an error mentioning that it attempted to connect but the variable does not exist.
I made a temporal fix with a sort of link statement (LinkVarComp bellow) which creates an explicit component with the output being equal to the input (and some additional things as scaling and a shift which could be useful for linear equations), but i am worried that this would add unnecessary computations/design variables/constraints.
Is there an easier/better workaround? (maybe by allowing variables to have multiple names?) what could be the best practice to just have a variable with a different name equal to a previous output/input?
A simple example:
import openmdao.api as om
model = om.Group()
model.add_subsystem('xcomp',subsys=om.IndepVarComp(name='x',val=np.zeros((3,2))),promotes_outputs=['*'])
model.connect('x','y')
p = om.Problem(model)
p.setup(force_alloc_complex=True)
p.set_val('x', np.array([[1.0 ,3],[10 ,-5],[0,3.1]]))
p.run_model()
Crashes with error
NameError: <model> <class Group>: Attempted to connect from 'x' to 'y', but 'y' doesn't exist.
While this works if using the following LinkVarComp component (but i suppose that adding new variables and computations)
import openmdao.api as om
import numpy as np
from math import prod
class LinkVarComp(om.ExplicitComponent):
"""
Component containing
"""
def initialize(self):
"""
Declare component options.
"""
self.options.declare('shape', types=(int,tuple),default=1)
self.options.declare('scale', types=int,default=1)
self.options.declare('shift', types=float,default=0.)
self.options.declare('input_default', types=float,default=0.)
self.options.declare('input_name', types=str,default='x')
self.options.declare('output_name', types=str,default='y')
self.options.declare('output_default', types=float,default=0.)
self.options.declare('input_units', types=(str,None),default=None)
self.options.declare('output_units', types=(str,None),default=None)
def setup(self):
self.add_input(name=self.options['input_name'],val=self.options['input_default'],shape=self.options['shape'],units=self.options['input_units'])
self.add_output(name=self.options['output_name'],val=self.options['output_default'],shape=self.options['shape'],units=self.options['output_units'])
if type(self.options['shape']) == int:
n = self.options['shape']
else:
n =prod( self.options['shape'])
ar = np.arange(n)
self.declare_partials(of=self.options['output_name'] , wrt=self.options['input_name'], rows=ar, cols=ar,val=self.options['scale'])
def compute(self, inputs, outputs):
outputs[self.options['output_name']] = self.options['scale']*inputs[self.options['input_name']] + self.options['shift']
model = om.Group()
model.add_subsystem('xcomp',subsys=om.IndepVarComp(name='x',val=np.zeros((3,2))),promotes_outputs=['*'])
model.add_subsystem('link', LinkVarComp(shape=(3,2)),
promotes_inputs=['*'],
promotes_outputs=['*'])
p = om.Problem(model)
p.setup(force_alloc_complex=True)
p.set_val('x', np.array([[1.0 ,3],[10 ,-5],[0,3.1]]))
p.run_model()
print(p['y'])
Outputing the expected:
[[ 1. 3. ]
[10. -5. ]
[ 0. 3.1]]
In OpenMDAO, you can not have the same variable take on two separate names. Thats simply not allowed.
The solution you came up with is effectively creating a separate component to hold a copy of the output. That works. You could use an ExecComp to have the same effect with a little less code:
import numpy as np
import openmdao.api as om
model = om.Group()
model.add_subsystem('xcomp',subsys=om.IndepVarComp(name='x',val=np.zeros((3,2))),promotes_outputs=['*'])
model.add_subsystem('ycomp', om.ExecComp("y=x", shape=(3,2)), promotes=['*'])
p = om.Problem(model)
p.setup(force_alloc_complex=True)
p.set_val('x', np.array([[1.0 ,3],[10 ,-5],[0,3.1]]))
p.run_model()
print(p['x'])
print(p['y'])
In general, I probably wouldn't actually do this myself. It seems kind of wasteful. Instead, I would modify my post-processing script to look for y and if it didn't find it to then grab x instead.

Ocaml - global vs local variable

I wanted to create a global variable called result that uses 5 string concatenations to create a string containing 9 times the string start, separated by commas.
I have two pieces of code, only the second one declares a global variable.
For some reason it's not registering easily in my brain... Is it just that i used a let in so result in the first piece of code is a local variable? Is there a more detailed explanation for this?
let start = "ab";;
let result = start ^ "," in
let result = result ^ result in
let result = result ^ result in
let result = result ^ result in
let result = result ^ start in
result;;
- : string = "ab,ab,ab,ab,ab,ab,ab,ab,ab"
let result =
let result = start ^ "," in
let result = result ^ result in
let result = result ^ result in
let result = result ^ result in
let result = result ^ start in
result;;
val result : string = "ab,ab,ab,ab,ab,ab,ab,ab,ab"
Let me to be a little bit boring person. There are no local and global variables in OCaml. This concept came from languages with different scoping rules. Also, the word "variable" itself should be taken with care. Its meaning was perverted by C-like languages. The original, mathematical, meaning of this word corresponds to a name of some mathematical object, that is used inside a formula, that represent a range of such values. In C-like languages, a variable is confused with the memory cell, that can change in time. So, to avoid the confusion let's use a more accurate terminology. Let's use word name instead of variable. Since, variables... sorry names are not memory cells, there is nothing to create. When you're using one of the let syntaxes, you're actually creating a binding, i.e., an association between a name and a value. The let <name> = <expr-1> in <expr-2> binds a value of the in the scope of the <expr-2> expression. The let <name> = <expr-1> in <expr-2> is by itself is also an expression, so, for example <expr-2> can also contain let ... in ... constructs inside, e.g.,
let a = 1 in
let b = a + 1 in
let c = b + 1 in
a + b + c
I especially, indented the code in non-idiomatic way to highlight the syntactic structure of the expression. OCaml also allows to use a name, that is already bound in the scope. The new binding will hide the existing one (that is not allowed in C, for example), e.g.,
let a = a + 1 in
let a = a + 1 in
let a = a + 1 in
a + a + a
Finally, the top-level (aka module level) let-binding (called definition in OCaml parlance), has the syntax: let <name> = <expr>, note that there is no in here. The definition binds the <name> to a result of the evaluation of <expr> in the lexical scope that extends form the point of definition to the end of the enclosing module. When you're implementing a module, you must use let <name> = <expr> to bind your code to names (you may omit name by using _). It is a little bit different from the interactive toplevel (interactive ocaml program), that actually accepts an expression, and evaluates it. For example,
let result = start ^ "," in
let result = result ^ result in
let result = result ^ result in
let result = result ^ result in
let result = result ^ start in
result
Is not a valid OCaml program (something that can be put into an ml file and compiled). Because it is an expression, not a module definition.
Is it just that i used a let in so result in the first piece of code is a local variable?
Pretty much. The syntax to define a global variable is let variable = expression without an in. The syntax to define a local variable is let variable = expression in expression which will define variable local to the expression after the in.
When you have let ... in, you're declaring a local variable. When you have just let by itself (at the top level of a module), you're declaring a global name of the module. (That is, a name that can be exported from the module.)
Your first example consists entirely of let ... in. So there is no top-level name declared.
Your second example has one let by itself, followed by several occurrences of let ... in. So it declares a top-level name result.

idl: pass keyword dynamically to isa function to test structure read by read_csv

I am using IDL 8.4. I want to use isa() function to determine input type read by read_csv(). I want to use /number, /integer, /float and /string as some field I want to make sure float, other to be integer and other I don't care. I can do like this, but it is not very readable to human eye.
str = read_csv(filename, header=inheader)
; TODO check header
if not isa(str.(0), /integer) then stop
if not isa(str.(1), /number) then stop
if not isa(str.(2), /float) then stop
I am hoping I can do something like
expected_header = ['id', 'x', 'val']
expected_type = ['/integer', '/number', '/float']
str = read_csv(filename, header=inheader)
if not array_equal(strlowcase(inheader), expected_header) then stop
for i=0l,n_elements(expected_type) do
if not isa(str.(i), expected_type[i]) then stop
endfor
the above doesn't work, as '/integer' is taken literally and I guess isa() is looking for named structure. How can you do something similar?
Ideally I want to pick expected type based on header read from file, so that script still works as long as header specifies expected field.
EDIT:
my tentative solution is to write a wrapper for ISA(). Not very pretty, but does what I wanted... if there is cleaner solution , please let me know.
Also, read_csv is defined to return only one of long, long64, double and string, so I could write function to test with this limitation. but I just wanted to make it to work in general so that I can reuse them for other similar cases.
function isa_generic,var,typ
; calls isa() http://www.exelisvis.com/docs/ISA.html with keyword
; if 'n', test /number
; if 'i', test /integer
; if 'f', test /float
; if 's', test /string
if typ eq 'n' then return, isa(var, /number)
if typ eq 'i' then then return, isa(var, /integer)
if typ eq 'f' then then return, isa(var, /float)
if typ eq 's' then then return, isa(var, /string)
print, 'unexpected typename: ', typ
stop
end
IDL has some limited reflection abilities, which will do exactly what you want:
expected_types = ['integer', 'number', 'float']
expected_header = ['id', 'x', 'val']
str = read_csv(filename, header=inheader)
if ~array_equal(strlowcase(inheader), expected_header) then stop
foreach type, expected_types, index do begin
if ~isa(str.(index), _extra=create_struct(type, 1)) then stop
endforeach
It's debatable if this is really "easier to read" in your case, since there are only three cases to test. If there were 500 cases, it would be a lot cleaner than writing 500 slightly different lines.
This snipped used some rather esoteric IDL features, so let me explain what's happening a bit:
expected_types is just a list of (string) keyword names in the order they should be used.
The foreach part iterates over expected_types, putting the keyword string into the type variable and the iteration count into index.
This is equivalent to using for index = 0, n_elements(expected_types) - 1 do and then using expected_types[index] instead of type, but the foreach loop is easier to read IMHO. Reference here.
_extra is a special keyword that can pass a structure as if it were a set of keywords. Each of the structure's tags is interpreted as a keyword. Reference here.
The create_struct function takes one or more pairs of (string) tag names and (any type) values, then returns a structure with those tag names and values. Reference here.
Finally, I replaced not (bitwise not) with ~ (logical not). This step, like foreach vs for, is not necessary in this instance, but can avoid headache when debugging some types of code, where the distinction matters.
--
Reflective abilities like these can do an awful lot, and come in super handy. They're work-horses in other languages, but IDL programmers don't seem to use them as much. Here's a quick list of common reflective features I use in IDL, with links to the documentation for each:
create_struct - Create a structure from (string) tag names and values.
n_tags - Get the number of tags in a structure.
_extra, _strict_extra, and _ref_extra - Pass keywords by structure or reference.
call_function - Call a function by its (string) name.
call_procedure - Call a procedure by its (string) name.
call_method - Call a method (of an object) by its (string) name.
execute - Run complete IDL commands stored in a string.
Note: Be very careful using the execute function. It will blindly execute any IDL statement you (or a user, file, web form, etc.) feed it. Never ever feed untrusted or web user input to the IDL execute function.
You can't access the keywords quite like that, but there is a typename parameter to ISA that might be useful. This is untested, but should work:
expected_header = ['id', 'x', 'val']
expected_type = ['int', 'long', 'float']
str = read_cv(filename, header=inheader)
if not array_equal(strlowcase(inheader), expected_header) then stop
for i = 0L, n_elemented(expected_type) - 1L do begin
if not isa(str.(i), expected_type[i]) then stop
endfor

Referencing a type parameter as a function parameter in Julia

I'm trying to make an "integer mod p" type in Julia. (I'm sure there's already a package for this, it's just a personal exercise.)
type Intp{p}
v::Int8
end
function add(a::Intp{p},b::Intp{p})
return Intp{p}((a.v + b.v) % p)
end
I'm getting an error when defining add that says p is not defined. How do I reference p from inside add?
(Note: I could do something like
type Intp
v::Int8
p
end
function add(a::Intp,b::Intp)
return Intp((a.v + b.v) % a.p,p)
end
but this would require that p be stored with every single number. I feel like this would be inefficient, and I have my mind on generalizations where it would be really inefficient. I would rather p just be specified once, for the type, and referenced in functions that take things of that type as arguments.)
Your first example is very close, but you need to include {p} between the method name and the signature like this:
function add{p}(a::Intp{p},b::Intp{p})
return Intp{p}((a.v + b.v) % p)
end
Otherwise, you are writing a method for a pair of Intp{p} values where p is whatever the current specific value of p may be – which, in your case, happens to be no value at all, hence the error message. So the general signature of a Julia method is:
method name
type parameters in { } (optional)
arguments in ( )

Map as a global variable in Ocaml

I have a question about manipulating a map as a global variable. In the beginning of my file, I have:
module IntOrd = struct type t = int let compare = ( - ) end
module IntMap = Map.Make( IntOrd )
Then I want to declare a global variable by let variables = IntMap.empty, then variables will be modified in some functions in this file. For instance, in a function let analyze (p: s_program) : unit = I want to fill in variables with some values in p. But I don't see how to do it, because it seems that I could not modify variables anymore; IntMap.add : key -> 'a -> 'a t -> 'a t would not work either because it does not change directly the values.
Do I have to make this global variable as a reference?
Could anyone help? Thank you very much
Well, you kind of answered your own question :). You want it to be a variable. Ocaml is functional, let x = ... does not declare a variable (it's a constant binding); to get a variable you need to make a reference. And then indeed you can modify it in your functions as in:
variables := IntMap.add foo bar !variables
Yes, if you want to be able to modify the map, you'll have to make it a reference. Then you can change it using variables := IntMap.add foo bar !variables.

Resources